From 35a5d63034f88032206392c45f2fce3ca2d6b0a4 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Mon, 23 Sep 2024 14:59:05 -0700 Subject: [PATCH 01/21] initial commit, linter come at me --- CHANGELOG.md | 4 + F1_22/f1.py | 4 +- F1_23/f1_23.py | 5 +- atomic_heart/atomicheart.py | 3 +- cities_skylines_2/citiesskylines2.py | 2 +- counterstrike2/cs2.py | 3 +- cyberpunk2077/cyberpunk2077.py | 3 +- dota2/dota2.py | 3 +- forza5/forza5.py | 3 +- grid_legends/grid_legends.py | 2 +- harness_utils/keras_service.py | 155 +++++++++++++++--- returnal/returnal.py | 3 +- rocket_league/rocket_league.py | 3 +- .../shadowofthetombraider.py | 2 +- stellaris/stellaris.py | 2 +- .../the_last_of_us_part_i.py | 3 +- total_war_pharaoh/total_war_pharaoh/twp.py | 2 +- total_war_warhammer_iii/twwh3.py | 3 +- 18 files changed, 151 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb910ca..73432c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. Changes are grouped by the date they are merged to the main branch of the repository and are ordered from newest to oldest. Dates use the ISO 8601 extended calendar date format, i.e. YYYY-MM-DD. +## 2024-09-23 + +- Add screen splitting to Keras Service. + ## 2024-9-20 - Add Grid Legends test harness. diff --git a/F1_22/f1.py b/F1_22/f1.py index 7f2d0fe..3ce77ef 100644 --- a/F1_22/f1.py +++ b/F1_22/f1.py @@ -158,9 +158,7 @@ console.setFormatter(formatter) logging.getLogger("").addHandler(console) args = get_args() -kerasService = KerasService( - args.keras_host, args.keras_port, os.path.join(LOG_DIRECTORY, "screenshot.jpg") -) +kerasService = KerasService(args.keras_host, args.keras_port) try: start_time, end_time = run_benchmark() diff --git a/F1_23/f1_23.py b/F1_23/f1_23.py index e4e19b4..3cb0b6e 100644 --- a/F1_23/f1_23.py +++ b/F1_23/f1_23.py @@ -228,10 +228,7 @@ parser.add_argument( "--kerasPort", dest="keras_port", help="Port for Keras OCR service", required=True ) args = parser.parse_args() -kerasService = KerasService( - args.keras_host, args.keras_port, os.path.join( - LOG_DIRECTORY, "screenshot.jpg") -) +kerasService = KerasService(args.keras_host, args.keras_port) try: start_time, end_time = run_benchmark() diff --git a/atomic_heart/atomicheart.py b/atomic_heart/atomicheart.py index bf13aba..1f446a4 100644 --- a/atomic_heart/atomicheart.py +++ b/atomic_heart/atomicheart.py @@ -141,8 +141,7 @@ parser.add_argument("--kerasHost", dest="keras_host", parser.add_argument("--kerasPort", dest="keras_port", help="Port for Keras OCR service", required=True) args = parser.parse_args() -kerasService = KerasService(args.keras_host, args.keras_port, os.path.join( - LOG_DIRECTORY, "screenshot.jpg")) +kerasService = KerasService(args.keras_host, args.keras_port) try: start_time, end_time = run_benchmark() diff --git a/cities_skylines_2/citiesskylines2.py b/cities_skylines_2/citiesskylines2.py index 324c9fb..00c29f5 100644 --- a/cities_skylines_2/citiesskylines2.py +++ b/cities_skylines_2/citiesskylines2.py @@ -123,7 +123,7 @@ def main(): parser.add_argument("--kerasPort", dest="keras_port", help="Port for Keras OCR service", required=True) args = parser.parse_args() - keras_service = KerasService(args.keras_host, args.keras_port, LOG_DIR.joinpath("screenshot.jpg")) + keras_service = KerasService(args.keras_host, args.keras_port) test_start_time, test_end_time = run_benchmark(keras_service) resolution = read_current_resolution() diff --git a/counterstrike2/cs2.py b/counterstrike2/cs2.py index b9df156..f8829bd 100644 --- a/counterstrike2/cs2.py +++ b/counterstrike2/cs2.py @@ -125,8 +125,7 @@ def main(): help="Port for Keras OCR service", required=True) args = parser.parse_args() - keras_service = KerasService(args.keras_host, args.keras_port, os.path.join( - LOG_DIR, "screenshot.jpg")) + keras_service = KerasService(args.keras_host, args.keras_port) start_time, end_time = run_benchmark(keras_service) diff --git a/cyberpunk2077/cyberpunk2077.py b/cyberpunk2077/cyberpunk2077.py index e41580d..2652bd6 100644 --- a/cyberpunk2077/cyberpunk2077.py +++ b/cyberpunk2077/cyberpunk2077.py @@ -126,8 +126,7 @@ console.setFormatter(formatter) logging.getLogger('').addHandler(console) args = get_args() -kerasService = KerasService(args.keras_host, args.keras_port, os.path.join( - LOG_DIRECTORY, "screenshot.jpg")) +kerasService = KerasService(args.keras_host, args.keras_port) try: start_time, end_time = run_benchmark() diff --git a/dota2/dota2.py b/dota2/dota2.py index e464634..7bda0ed 100644 --- a/dota2/dota2.py +++ b/dota2/dota2.py @@ -37,8 +37,7 @@ console.setFormatter(formatter) logging.getLogger('').addHandler(console) args = get_args() -kerasService = KerasService(args.keras_host, args.keras_port, os.path.join( - LOG_DIRECTORY, "screenshot.jpg")) +kerasService = KerasService(args.keras_host, args.keras_port) def start_game(): diff --git a/forza5/forza5.py b/forza5/forza5.py index d4d2465..4c21d26 100644 --- a/forza5/forza5.py +++ b/forza5/forza5.py @@ -133,8 +133,7 @@ parser.add_argument("--kerasHost", dest="keras_host", parser.add_argument("--kerasPort", dest="keras_port", help="Port for Keras OCR service", required=True) args = parser.parse_args() -kerasService = KerasService(args.keras_host, args.keras_port, os.path.join( - LOG_DIRECTORY, "screenshot.jpg")) +kerasService = KerasService(args.keras_host, args.keras_port) try: start_time, end_time = run_benchmark() diff --git a/grid_legends/grid_legends.py b/grid_legends/grid_legends.py index e451f6b..3fbde15 100644 --- a/grid_legends/grid_legends.py +++ b/grid_legends/grid_legends.py @@ -142,7 +142,7 @@ def run_benchmark(keras_service): def main(): """entry point""" args = get_args() - keras_service = KerasService(args.keras_host, args.keras_port, LOG_DIR.joinpath("screenshot.jpg")) + keras_service = KerasService(args.keras_host, args.keras_port) start_time, end_time = run_benchmark(keras_service) elapsed_test_time = round((end_time - start_time), 2) logging.info("Benchmark took %f seconds", elapsed_test_time) diff --git a/harness_utils/keras_service.py b/harness_utils/keras_service.py index 7ac5032..e5b18ac 100644 --- a/harness_utils/keras_service.py +++ b/harness_utils/keras_service.py @@ -1,45 +1,90 @@ """Allows accessing Keras Service if available.""" +import io import json import logging -import os import time import mss import cv2 import requests import numpy as np +from enum import Enum +from dataclasses import dataclass logging.getLogger("requests").setLevel(logging.WARNING) logging.getLogger("urllib3").setLevel(logging.WARNING) DEFAULT_TIMEOUT = 120.0 + +class ScreenShotDivideMethod(Enum): + """split method""" + horizontal = "horizontal" + vertical = "vertical" + quadrant = "quadrant" + none = "none" + + +class ScreenShotQuadrant(Enum): + """split arguments""" + TOP = 1 + BOTTOM = 2 + LEFT = 1 + RIGHT = 2 + TOP_LEFT = 1 + TOP_RIGHT = 2 + BOTTOM_LEFT = 3 + BOTTOM_RIGHT = 4 + + +class FrameDivideException(ValueError): + """Exception indicating was encountered while trying to divide a frame""" + + +@dataclass +class ScreenSplitConfig: + """data class to contain config for taking splitting a screen shot""" + divide_method: ScreenShotDivideMethod + quadrant: ScreenShotQuadrant + + class KerasService(): """Sets up connection to a Keras service and provides methods to use it""" + def __init__( self, ip_addr: str, port: int | str, - screenshot_path: str | os.PathLike, timeout: float = DEFAULT_TIMEOUT) -> None: self.ip_addr = ip_addr self.port = str(port) self.url = f"http://{ip_addr}:{str(port)}/process" - self.screenshot_path = screenshot_path self.timeout = timeout - def _capture_screenshot(self) -> None: + def _capture_screenshot(self, split_config: ScreenSplitConfig) -> io.BytesIO: + screenshot = None with mss.mss() as sct: monitor_1 = sct.monitors[1] # Identify the display to capture - screen = np.array(sct.grab(monitor_1)) - screen = cv2.cvtColor(screen, cv2.COLOR_RGB2GRAY) - cv2.imwrite(str(self.screenshot_path), screen) + screenshot = np.array(sct.grab(monitor_1)) + screenshot = cv2.cvtColor(screenshot, cv2.COLOR_RGB2GRAY) - def _query_service(self, word: str, report_file: any) -> any: + if split_config.divide_method == ScreenShotDivideMethod.horizontal: + screenshot = self._divide_horizontal(screenshot) + elif split_config.divide_method == ScreenShotDivideMethod.vertical: + screenshot = self._divide_vertical(screenshot) + elif split_config.divide_method == ScreenShotDivideMethod.quadrant: + screenshot = self._divide_in_four(screenshot) + elif split_config.divide_method == ScreenShotDivideMethod.none: + screenshot = screenshot + + _, encoded_image = cv2.imencode('.jpg', screenshot) + return io.BytesIO(encoded_image) + + def _query_service(self, word: str, image_bytes: io.BytesIO) -> any: try: keras_response = requests.post( self.url, data={"word": word}, - files={"file": report_file}, + files={"file": image_bytes}, timeout=self.timeout ) @@ -53,11 +98,56 @@ class KerasService(): except requests.exceptions.Timeout: return None - def capture_screenshot_find_word(self, word: str) -> any: - """Take a screenshot and try to find the given word within it.""" - self._capture_screenshot() - with open(self.screenshot_path, "rb") as report_file: - return self._query_service(word, report_file) + def _divide_horizontal(self, screenshot, quadrant: ScreenShotQuadrant): + """divide the screenshot horizontally""" + height, _, _ = screenshot.shape + if quadrant == ScreenShotQuadrant.TOP: + return screenshot[0:int(height/2), :] + elif quadrant == ScreenShotQuadrant.BOTTOM: + return screenshot[int(height/2):int(height), :] + else: + raise FrameDivideException( + f"Unrecognized quadrant for horizontal: {self.quadrant}") + + def _divide_vertical(self, screenshot, quadrant: ScreenShotQuadrant): + """divide the screenshot vertically""" + _, width, _ = screenshot.shape + if self.quadrant == quadrant.LEFT: + return screenshot[:, 0:int(width/2)] + elif self.quadrant == quadrant.RIGHT: + return screenshot[:, int(width/2):int(width)] + else: + raise FrameDivideException( + f"Unrecognized quadrant for vertical: {self.quadrant}") + + def _divide_in_four(self, screenshot, quadrant: ScreenShotQuadrant): + """divide the screenshot in four quadrants""" + height, width, _ = screenshot.shape + if self.quadrant == ScreenShotQuadrant.TOP_LEFT: + return screenshot[0:int(height/2), 0:int(width/2)] + elif self.quadrant == ScreenShotQuadrant.TOP_RIGHT: + return screenshot[0:int(height/2), int(width/2):int(width)] + elif self.quadrant == ScreenShotQuadrant.BOTTOM_LEFT: + return screenshot[int(height/2):int(height), 0:int(width/2)] + elif self.quadrant == ScreenShotQuadrant.BOTTOM_RIGHT: + return screenshot[int(height/2):int(height), int(width/2):int(width)] + else: + raise FrameDivideException( + f"Unrecognized quadrant for in four: {self.quadrant}") + + def _look_for_word( + self, word: str, + attempts: int = 1, + interval: float = 0.0, + split_config: ScreenSplitConfig = None) -> bool: + """implementation of look for word""" + for _ in range(attempts): + image_bytes = self._capture_screenshot(split_config) + result = self._query_service(word, image_bytes) + if result is not None: + return result + time.sleep(interval) + return None def look_for_word(self, word: str, attempts: int = 1, interval: float = 0.0) -> bool: """Takes a screenshot of the monitor and searches for a given word. @@ -65,23 +155,40 @@ class KerasService(): of attempts is reached. Will return early if the query result comes back with a match. """ - for _ in range(attempts): - result = self.capture_screenshot_find_word(word) + return self._look_for_word(word, attempts, interval, ScreenSplitConfig(divide_method=ScreenShotDivideMethod.none)) + + def look_for_word(self, word: str, attempts: int = 1, interval: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: + """Overload for look_for_word but allows for screen splitting + which will look for a word in only part of the screen + """ + return self._look_for_word(word, attempts, interval, split_config) + + def _wait_for_word( + self, + word: str, + interval: float, + timeout: float, + split_config: ScreenSplitConfig) -> bool: + """implementation of wait for word""" + search_start_time = time.time() + while time.time() - search_start_time < timeout: + image_bytes = self._capture_screenshot(split_config) + result = self._query_service(word, image_bytes) if result is not None: return result time.sleep(interval) return None - def wait_for_word(self, word: str, interval: float = 0.0, timeout: float = 0.0): + def wait_for_word(self, word: str, interval: float = 0.0, timeout: float = 0.0) -> bool: """Takes a screenshot of the monitor and searches for a given word. Will look for the word at a given time interval until the specified timeout has been exceeded. Will return early if the query result comes back with a match. """ - search_start_time = time.time() - while time.time() - search_start_time < timeout: - result = self.capture_screenshot_find_word(word) - if result is not None: - return result - time.sleep(interval) - return None + return self._wait_for_word(word, interval, timeout, ScreenSplitConfig(divide_method=ScreenShotDivideMethod.none)) + + def wait_for_word(self, word: str, interval: float = 0.0, timeout: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: + """Overload for wait_for_word but allows for screen splitting + which will look for a word in only part of the screen + """ + return self._wait_for_word(word, interval, timeout, split_config) diff --git a/returnal/returnal.py b/returnal/returnal.py index 26030f5..ed7b3c4 100644 --- a/returnal/returnal.py +++ b/returnal/returnal.py @@ -156,8 +156,7 @@ console.setFormatter(formatter) logging.getLogger('').addHandler(console) args = get_args() -kerasService = KerasService( - args.keras_host, args.keras_port, os.path.join(LOG_DIRECTORY, "screenshot.jpg")) +kerasService = KerasService(args.keras_host, args.keras_port) try: start_time, end_time = run_benchmark() diff --git a/rocket_league/rocket_league.py b/rocket_league/rocket_league.py index a929c6c..f9dd4da 100644 --- a/rocket_league/rocket_league.py +++ b/rocket_league/rocket_league.py @@ -38,8 +38,7 @@ console.setFormatter(formatter) logging.getLogger('').addHandler(console) args = get_args() -kerasService = KerasService(args.keras_host, args.keras_port, os.path.join( - LOG_DIRECTORY, "screenshot.jpg")) +kerasService = KerasService(args.keras_host, args.keras_port) def get_run_game_id_command(game_id: int) -> str: diff --git a/shadowofthetombraider/shadowofthetombraider.py b/shadowofthetombraider/shadowofthetombraider.py index eb967f3..d72ff94 100644 --- a/shadowofthetombraider/shadowofthetombraider.py +++ b/shadowofthetombraider/shadowofthetombraider.py @@ -52,7 +52,7 @@ def run_benchmark(): start_game() args = get_args() - keras_service = KerasService(args.keras_host, args.keras_port, LOG_DIR.joinpath("screenshot.jpg")) + keras_service = KerasService(args.keras_host, args.keras_port) am = ArtifactManager(LOG_DIR) if keras_service.wait_for_word(word="options", timeout=30, interval=1) is None: diff --git a/stellaris/stellaris.py b/stellaris/stellaris.py index 120cc23..ba9ac0e 100644 --- a/stellaris/stellaris.py +++ b/stellaris/stellaris.py @@ -61,7 +61,7 @@ def console_command(command): def run_benchmark(keras_host, keras_port): """Starts the benchmark""" - keras_service = KerasService(keras_host, keras_port, str(LOG_DIR.joinpath("screenshot.jpg"))) + keras_service = KerasService(keras_host, keras_port) copy_benchmarkfiles() copy_benchmarksave() start_game() diff --git a/the_last_of_us_part_i/the_last_of_us_part_i.py b/the_last_of_us_part_i/the_last_of_us_part_i.py index f930eb9..130b437 100644 --- a/the_last_of_us_part_i/the_last_of_us_part_i.py +++ b/the_last_of_us_part_i/the_last_of_us_part_i.py @@ -126,8 +126,7 @@ console.setFormatter(formatter) logging.getLogger('').addHandler(console) args = get_args() -kerasService = KerasService( - args.keras_host, args.keras_port, os.path.join(LOG_DIRECTORY, "screenshot.jpg")) +kerasService = KerasService(args.keras_host, args.keras_port) try: start_time, end_time = run_benchmark() diff --git a/total_war_pharaoh/total_war_pharaoh/twp.py b/total_war_pharaoh/total_war_pharaoh/twp.py index 4f9ed7f..e57407d 100644 --- a/total_war_pharaoh/total_war_pharaoh/twp.py +++ b/total_war_pharaoh/total_war_pharaoh/twp.py @@ -167,7 +167,7 @@ def main(): parser.add_argument("--kerasPort", dest="keras_port", help="Port for Keras OCR service", required=True) args = parser.parse_args() - keras_service = KerasService(args.keras_host, args.keras_port, LOG_DIR.joinpath("screenshot.jpg")) + keras_service = KerasService(args.keras_host, args.keras_port) start_time, endtime = run_benchmark(keras_service) height, width = read_current_resolution() report = { diff --git a/total_war_warhammer_iii/twwh3.py b/total_war_warhammer_iii/twwh3.py index db72241..0a38723 100644 --- a/total_war_warhammer_iii/twwh3.py +++ b/total_war_warhammer_iii/twwh3.py @@ -172,8 +172,7 @@ parser.add_argument("--kerasHost", dest="keras_host", parser.add_argument("--kerasPort", dest="keras_port", help="Port for Keras OCR service", required=True) args = parser.parse_args() -kerasService = KerasService(args.keras_host, args.keras_port, os.path.join( - LOG_DIRECTORY, "screenshot.jpg")) +kerasService = KerasService(args.keras_host, args.keras_port) try: start_time, endtime = run_benchmark() From 6b20bf62a32dbbf0ee42d8f2cdd9b6e2bcb8c49e Mon Sep 17 00:00:00 2001 From: Nikolas Date: Mon, 23 Sep 2024 15:04:38 -0700 Subject: [PATCH 02/21] linter sacrafice --- harness_utils/keras_service.py | 38 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/harness_utils/keras_service.py b/harness_utils/keras_service.py index e5b18ac..ff6a2d9 100644 --- a/harness_utils/keras_service.py +++ b/harness_utils/keras_service.py @@ -18,10 +18,10 @@ DEFAULT_TIMEOUT = 120.0 class ScreenShotDivideMethod(Enum): """split method""" - horizontal = "horizontal" - vertical = "vertical" - quadrant = "quadrant" - none = "none" + HORIZONTAL = "horizontal" + VERTICAL = "vertical" + QUADRANT = "quadrant" + NONE = "none" class ScreenShotQuadrant(Enum): @@ -67,13 +67,13 @@ class KerasService(): screenshot = np.array(sct.grab(monitor_1)) screenshot = cv2.cvtColor(screenshot, cv2.COLOR_RGB2GRAY) - if split_config.divide_method == ScreenShotDivideMethod.horizontal: + if split_config.divide_method == ScreenShotDivideMethod.HORIZONTAL: screenshot = self._divide_horizontal(screenshot) - elif split_config.divide_method == ScreenShotDivideMethod.vertical: + elif split_config.divide_method == ScreenShotDivideMethod.VERTICAL: screenshot = self._divide_vertical(screenshot) - elif split_config.divide_method == ScreenShotDivideMethod.quadrant: + elif split_config.divide_method == ScreenShotDivideMethod.QUADRANT: screenshot = self._divide_in_four(screenshot) - elif split_config.divide_method == ScreenShotDivideMethod.none: + elif split_config.divide_method == ScreenShotDivideMethod.NONE: screenshot = screenshot _, encoded_image = cv2.imencode('.jpg', screenshot) @@ -107,33 +107,33 @@ class KerasService(): return screenshot[int(height/2):int(height), :] else: raise FrameDivideException( - f"Unrecognized quadrant for horizontal: {self.quadrant}") + f"Unrecognized quadrant for horizontal: {quadrant}") def _divide_vertical(self, screenshot, quadrant: ScreenShotQuadrant): """divide the screenshot vertically""" _, width, _ = screenshot.shape - if self.quadrant == quadrant.LEFT: + if quadrant == quadrant.LEFT: return screenshot[:, 0:int(width/2)] - elif self.quadrant == quadrant.RIGHT: + elif quadrant == quadrant.RIGHT: return screenshot[:, int(width/2):int(width)] else: raise FrameDivideException( - f"Unrecognized quadrant for vertical: {self.quadrant}") + f"Unrecognized quadrant for vertical: {quadrant}") def _divide_in_four(self, screenshot, quadrant: ScreenShotQuadrant): """divide the screenshot in four quadrants""" height, width, _ = screenshot.shape - if self.quadrant == ScreenShotQuadrant.TOP_LEFT: + if quadrant == ScreenShotQuadrant.TOP_LEFT: return screenshot[0:int(height/2), 0:int(width/2)] - elif self.quadrant == ScreenShotQuadrant.TOP_RIGHT: + elif quadrant == ScreenShotQuadrant.TOP_RIGHT: return screenshot[0:int(height/2), int(width/2):int(width)] - elif self.quadrant == ScreenShotQuadrant.BOTTOM_LEFT: + elif quadrant == ScreenShotQuadrant.BOTTOM_LEFT: return screenshot[int(height/2):int(height), 0:int(width/2)] - elif self.quadrant == ScreenShotQuadrant.BOTTOM_RIGHT: + elif quadrant == ScreenShotQuadrant.BOTTOM_RIGHT: return screenshot[int(height/2):int(height), int(width/2):int(width)] else: raise FrameDivideException( - f"Unrecognized quadrant for in four: {self.quadrant}") + f"Unrecognized quadrant for in four: {quadrant}") def _look_for_word( self, word: str, @@ -155,7 +155,7 @@ class KerasService(): of attempts is reached. Will return early if the query result comes back with a match. """ - return self._look_for_word(word, attempts, interval, ScreenSplitConfig(divide_method=ScreenShotDivideMethod.none)) + return self._look_for_word(word, attempts, interval, ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE)) def look_for_word(self, word: str, attempts: int = 1, interval: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: """Overload for look_for_word but allows for screen splitting @@ -185,7 +185,7 @@ class KerasService(): has been exceeded. Will return early if the query result comes back with a match. """ - return self._wait_for_word(word, interval, timeout, ScreenSplitConfig(divide_method=ScreenShotDivideMethod.none)) + return self._wait_for_word(word, interval, timeout, ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE)) def wait_for_word(self, word: str, interval: float = 0.0, timeout: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: """Overload for wait_for_word but allows for screen splitting From cf0cbe8858b601084c428dfeb78d7d37085ad948 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Mon, 23 Sep 2024 15:07:48 -0700 Subject: [PATCH 03/21] almost all linting --- harness_utils/keras_service.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/harness_utils/keras_service.py b/harness_utils/keras_service.py index ff6a2d9..60025f8 100644 --- a/harness_utils/keras_service.py +++ b/harness_utils/keras_service.py @@ -68,13 +68,11 @@ class KerasService(): screenshot = cv2.cvtColor(screenshot, cv2.COLOR_RGB2GRAY) if split_config.divide_method == ScreenShotDivideMethod.HORIZONTAL: - screenshot = self._divide_horizontal(screenshot) + screenshot = self._divide_horizontal(screenshot, split_config.quadrant) elif split_config.divide_method == ScreenShotDivideMethod.VERTICAL: - screenshot = self._divide_vertical(screenshot) + screenshot = self._divide_vertical(screenshot, split_config.quadrant) elif split_config.divide_method == ScreenShotDivideMethod.QUADRANT: - screenshot = self._divide_in_four(screenshot) - elif split_config.divide_method == ScreenShotDivideMethod.NONE: - screenshot = screenshot + screenshot = self._divide_in_four(screenshot, split_config.quadrant) _, encoded_image = cv2.imencode('.jpg', screenshot) return io.BytesIO(encoded_image) @@ -103,7 +101,7 @@ class KerasService(): height, _, _ = screenshot.shape if quadrant == ScreenShotQuadrant.TOP: return screenshot[0:int(height/2), :] - elif quadrant == ScreenShotQuadrant.BOTTOM: + if quadrant == ScreenShotQuadrant.BOTTOM: return screenshot[int(height/2):int(height), :] else: raise FrameDivideException( @@ -114,7 +112,7 @@ class KerasService(): _, width, _ = screenshot.shape if quadrant == quadrant.LEFT: return screenshot[:, 0:int(width/2)] - elif quadrant == quadrant.RIGHT: + if quadrant == quadrant.RIGHT: return screenshot[:, int(width/2):int(width)] else: raise FrameDivideException( From 5a6c4b8ae50c4de00ed8f46124e71748af9c6379 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Mon, 23 Sep 2024 15:12:46 -0700 Subject: [PATCH 04/21] enjyo --- harness_utils/keras_service.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/harness_utils/keras_service.py b/harness_utils/keras_service.py index 60025f8..f1e625c 100644 --- a/harness_utils/keras_service.py +++ b/harness_utils/keras_service.py @@ -103,9 +103,8 @@ class KerasService(): return screenshot[0:int(height/2), :] if quadrant == ScreenShotQuadrant.BOTTOM: return screenshot[int(height/2):int(height), :] - else: - raise FrameDivideException( - f"Unrecognized quadrant for horizontal: {quadrant}") + raise FrameDivideException( + f"Unrecognized quadrant for horizontal: {quadrant}") def _divide_vertical(self, screenshot, quadrant: ScreenShotQuadrant): """divide the screenshot vertically""" @@ -114,8 +113,7 @@ class KerasService(): return screenshot[:, 0:int(width/2)] if quadrant == quadrant.RIGHT: return screenshot[:, int(width/2):int(width)] - else: - raise FrameDivideException( + raise FrameDivideException( f"Unrecognized quadrant for vertical: {quadrant}") def _divide_in_four(self, screenshot, quadrant: ScreenShotQuadrant): @@ -123,15 +121,14 @@ class KerasService(): height, width, _ = screenshot.shape if quadrant == ScreenShotQuadrant.TOP_LEFT: return screenshot[0:int(height/2), 0:int(width/2)] - elif quadrant == ScreenShotQuadrant.TOP_RIGHT: + if quadrant == ScreenShotQuadrant.TOP_RIGHT: return screenshot[0:int(height/2), int(width/2):int(width)] - elif quadrant == ScreenShotQuadrant.BOTTOM_LEFT: + if quadrant == ScreenShotQuadrant.BOTTOM_LEFT: return screenshot[int(height/2):int(height), 0:int(width/2)] - elif quadrant == ScreenShotQuadrant.BOTTOM_RIGHT: + if quadrant == ScreenShotQuadrant.BOTTOM_RIGHT: return screenshot[int(height/2):int(height), int(width/2):int(width)] - else: - raise FrameDivideException( - f"Unrecognized quadrant for in four: {quadrant}") + raise FrameDivideException( + f"Unrecognized quadrant for in four: {quadrant}") def _look_for_word( self, word: str, @@ -153,7 +150,8 @@ class KerasService(): of attempts is reached. Will return early if the query result comes back with a match. """ - return self._look_for_word(word, attempts, interval, ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE)) + empty_config = ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE, quadrant=ScreenShotQuadrant.TOP) + return self._look_for_word(word, attempts, interval, empty_config) def look_for_word(self, word: str, attempts: int = 1, interval: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: """Overload for look_for_word but allows for screen splitting @@ -183,7 +181,8 @@ class KerasService(): has been exceeded. Will return early if the query result comes back with a match. """ - return self._wait_for_word(word, interval, timeout, ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE)) + empty_config = ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE, quadrant=ScreenShotQuadrant.TOP) + return self._wait_for_word(word, interval, timeout, empty_config) def wait_for_word(self, word: str, interval: float = 0.0, timeout: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: """Overload for wait_for_word but allows for screen splitting From 27473cd7159adc617f031a934575b68c1cb50ff9 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Mon, 23 Sep 2024 15:25:56 -0700 Subject: [PATCH 05/21] update --- harness_utils/keras_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/harness_utils/keras_service.py b/harness_utils/keras_service.py index f1e625c..c03194f 100644 --- a/harness_utils/keras_service.py +++ b/harness_utils/keras_service.py @@ -153,7 +153,7 @@ class KerasService(): empty_config = ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE, quadrant=ScreenShotQuadrant.TOP) return self._look_for_word(word, attempts, interval, empty_config) - def look_for_word(self, word: str, attempts: int = 1, interval: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: + def look_for_word_split(self, word: str, attempts: int = 1, interval: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: """Overload for look_for_word but allows for screen splitting which will look for a word in only part of the screen """ @@ -184,7 +184,7 @@ class KerasService(): empty_config = ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE, quadrant=ScreenShotQuadrant.TOP) return self._wait_for_word(word, interval, timeout, empty_config) - def wait_for_word(self, word: str, interval: float = 0.0, timeout: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: + def wait_for_word_split(self, word: str, interval: float = 0.0, timeout: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: """Overload for wait_for_word but allows for screen splitting which will look for a word in only part of the screen """ From 4d7809d189716e23778138c17218b02dbac13da5 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Mon, 23 Sep 2024 15:30:52 -0700 Subject: [PATCH 06/21] plase --- harness_utils/keras_service.py | 64 +++++++++++----------------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/harness_utils/keras_service.py b/harness_utils/keras_service.py index c03194f..71ce1a4 100644 --- a/harness_utils/keras_service.py +++ b/harness_utils/keras_service.py @@ -68,11 +68,14 @@ class KerasService(): screenshot = cv2.cvtColor(screenshot, cv2.COLOR_RGB2GRAY) if split_config.divide_method == ScreenShotDivideMethod.HORIZONTAL: - screenshot = self._divide_horizontal(screenshot, split_config.quadrant) + screenshot = self._divide_horizontal( + screenshot, split_config.quadrant) elif split_config.divide_method == ScreenShotDivideMethod.VERTICAL: - screenshot = self._divide_vertical(screenshot, split_config.quadrant) + screenshot = self._divide_vertical( + screenshot, split_config.quadrant) elif split_config.divide_method == ScreenShotDivideMethod.QUADRANT: - screenshot = self._divide_in_four(screenshot, split_config.quadrant) + screenshot = self._divide_in_four( + screenshot, split_config.quadrant) _, encoded_image = cv2.imencode('.jpg', screenshot) return io.BytesIO(encoded_image) @@ -114,7 +117,7 @@ class KerasService(): if quadrant == quadrant.RIGHT: return screenshot[:, int(width/2):int(width)] raise FrameDivideException( - f"Unrecognized quadrant for vertical: {quadrant}") + f"Unrecognized quadrant for vertical: {quadrant}") def _divide_in_four(self, screenshot, quadrant: ScreenShotQuadrant): """divide the screenshot in four quadrants""" @@ -130,12 +133,13 @@ class KerasService(): raise FrameDivideException( f"Unrecognized quadrant for in four: {quadrant}") - def _look_for_word( - self, word: str, - attempts: int = 1, - interval: float = 0.0, - split_config: ScreenSplitConfig = None) -> bool: - """implementation of look for word""" + def look_for_word(self, word: str, attempts: int = 1, interval: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: + """Overload for look_for_word but allows for screen splitting + which will look for a word in only part of the screen + """ + if split_config is None: + split_config = ScreenSplitConfig( + divide_method=ScreenShotDivideMethod.NONE, quadrant=ScreenShotQuadrant.TOP) for _ in range(attempts): image_bytes = self._capture_screenshot(split_config) result = self._query_service(word, image_bytes) @@ -144,28 +148,15 @@ class KerasService(): time.sleep(interval) return None - def look_for_word(self, word: str, attempts: int = 1, interval: float = 0.0) -> bool: + def wait_for_word(self, word: str, interval: float = 0.0, timeout: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: """Takes a screenshot of the monitor and searches for a given word. - Will look for the word at a given time interval until the specified number - of attempts is reached. + Will look for the word at a given time interval until the specified timeout + has been exceeded. Will return early if the query result comes back with a match. """ - empty_config = ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE, quadrant=ScreenShotQuadrant.TOP) - return self._look_for_word(word, attempts, interval, empty_config) - - def look_for_word_split(self, word: str, attempts: int = 1, interval: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: - """Overload for look_for_word but allows for screen splitting - which will look for a word in only part of the screen - """ - return self._look_for_word(word, attempts, interval, split_config) - - def _wait_for_word( - self, - word: str, - interval: float, - timeout: float, - split_config: ScreenSplitConfig) -> bool: - """implementation of wait for word""" + if split_config is None: + split_config = ScreenSplitConfig( + divide_method=ScreenShotDivideMethod.NONE, quadrant=ScreenShotQuadrant.TOP) search_start_time = time.time() while time.time() - search_start_time < timeout: image_bytes = self._capture_screenshot(split_config) @@ -174,18 +165,3 @@ class KerasService(): return result time.sleep(interval) return None - - def wait_for_word(self, word: str, interval: float = 0.0, timeout: float = 0.0) -> bool: - """Takes a screenshot of the monitor and searches for a given word. - Will look for the word at a given time interval until the specified timeout - has been exceeded. - Will return early if the query result comes back with a match. - """ - empty_config = ScreenSplitConfig(divide_method=ScreenShotDivideMethod.NONE, quadrant=ScreenShotQuadrant.TOP) - return self._wait_for_word(word, interval, timeout, empty_config) - - def wait_for_word_split(self, word: str, interval: float = 0.0, timeout: float = 0.0, split_config: ScreenSplitConfig = None) -> bool: - """Overload for wait_for_word but allows for screen splitting - which will look for a word in only part of the screen - """ - return self._wait_for_word(word, interval, timeout, split_config) From c628f164ecc4ea09d09921d8cf5e5f8fec123163 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Mon, 23 Sep 2024 16:41:03 -0700 Subject: [PATCH 07/21] update readme --- harness_utils/README.md | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/harness_utils/README.md b/harness_utils/README.md index f28bdfd..a2f0608 100644 --- a/harness_utils/README.md +++ b/harness_utils/README.md @@ -78,6 +78,66 @@ Given the configuration and artifacts captured in the above code snippets, the r Contains class for instancing connection to a Keras Service and provides access to its web API. +### Usage: + +Import KerasService + +```python +from harness_utils.keras_service import KerasService +``` + +Instantiate a Keras + +```python +# usually your host and port will come from the script arguments +keras_service = KerasService(args.keras_host, args.keras_port) +``` + +You can look a word on the screen for a number of attempts, or wait for a word to appear on the screen for an amount of time. + +```python +# this will send a screenshot every 1 second to keras and look for the word "options" in it +# this function call will block until the word has been found returning True, or None once 30 seconds has passed +result = keras_service.wait_for_word("options", timeout=30, interval=1) + +# this will send a screenshot to keras every 1 second ato keras and look for the word "continue" in it +# this function call will block until it has tried 20 times +result = keras_service.look_for_word("continue", attempts=20, interval=1) +``` + +You can optionally check only half the screen, or 1/4 of the screen. This shrinks the amount of screenshot that Keras has to search for a word which means a faster result time. + +First import ScreenSplitConfig, ScreenShotDivideMethod, and ScreenShotQuadrant + +```python +from harness_utils.keras_service import KerasService, ScreenSplitConfig, ScreenShotDivideMethod, ScreenShotQuadrant +``` + +Then create your ScreenSplitConfig object and pass it to the look_for_word or wait_for_word functions. + +```python +# this config will split the screen horizontally and look in the top of the screen +ss_config = ScreenSplitConfig( + divide_method=ScreenShotDivideMethod.HORIZONTAL + quadrant=ScreenShotQuadrant.TOP +) + +# this one will split the screen vertically and look in the right of the screen +ss_config = ScreenSplitConfig( + divide_method=ScreenShotDivideMethod.VERTICAL + quadrant=ScreenShotQuadrant.RIGHT +) + +# ans this will split the screen into 4 and look in the bottom left of the screen +ss_config = ScreenSplitConfig( + divide_method=ScreenShotDivideMethod.QUADRANT + quadrant=ScreenShotQuadrant.BOTTOM_LEFT +) + +# pass the config to the function call +result = keras_service.wait_for_word("options", timeout=30, interval=1, split_config=ss_config) +``` + ## Output `output.py` From 8b68a92940ed242e6590042dd67399240638bb79 Mon Sep 17 00:00:00 2001 From: Arty Blue Date: Fri, 27 Sep 2024 08:44:34 -0700 Subject: [PATCH 08/21] results screenshot and saving of config file at the end of the run --- total_war_warhammer_iii/twwh3.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/total_war_warhammer_iii/twwh3.py b/total_war_warhammer_iii/twwh3.py index db72241..da1264a 100644 --- a/total_war_warhammer_iii/twwh3.py +++ b/total_war_warhammer_iii/twwh3.py @@ -2,6 +2,7 @@ from argparse import ArgumentParser import logging import os +from pathlib import Path import time import sys import pyautogui as gui @@ -21,12 +22,19 @@ from harness_utils.output import ( ) from harness_utils.steam import get_app_install_location from harness_utils.keras_service import KerasService +from harness_utils.artifacts import ArtifactManager, ArtifactType SCRIPT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) LOG_DIRECTORY = os.path.join(SCRIPT_DIRECTORY, "run") PROCESS_NAME = "Warhammer3.exe" STEAM_GAME_ID = 1142710 +APPDATA = os.getenv("APPDATA") +CONFIG_LOCATION = f"{APPDATA}\\The Creative Assembly\\Warhammer3\\scripts" +CONFIG_FILENAME = "preferences.script.txt" + +cfg = f"{CONFIG_LOCATION}\\{CONFIG_FILENAME}" + user.FAILSAFE = False @@ -63,6 +71,8 @@ def run_benchmark(): start_game() setup_start_time = time.time() time.sleep(5) + + am = ArtifactManager(LOG_DIRECTORY) result = kerasService.look_for_word("warning", attempts=10, interval=5) if not result: @@ -84,6 +94,8 @@ def run_benchmark(): gui.mouseUp() time.sleep(2) + am.take_screenshot("main.png", ArtifactType.CONFIG_IMAGE, "picture of basic settings") + result = kerasService.look_for_word("ad", attempts=10, interval=1) if not result: logging.info("Did not find the advanced menu. Did the game skip the intros?") @@ -96,6 +108,8 @@ def run_benchmark(): gui.mouseUp() time.sleep(0.5) + am.take_screenshot("advanced.png", ArtifactType.CONFIG_IMAGE, "picture of advanced settings") + result = kerasService.look_for_word("bench", attempts=10, interval=1) if not result: logging.info("Did not find the benchmark menu. Did the game skip the intros?") @@ -144,6 +158,9 @@ def run_benchmark(): # Wait 5 seconds for benchmark info time.sleep(5) + am.take_screenshot("result.png", ArtifactType.RESULTS_IMAGE, "benchmark results") + am.copy_file(Path(cfg), ArtifactType.RESULTS_TEXT, "preferences.script.txt") + # End the run elapsed_test_time = round(test_end_time - test_start_time, 2) logging.info("Benchmark took %f seconds", elapsed_test_time) From 46419f4c2d96fd490b5a06ee080338cffe40fac3 Mon Sep 17 00:00:00 2001 From: Arty Blue Date: Fri, 27 Sep 2024 09:57:51 -0700 Subject: [PATCH 09/21] fixed a typo in the config saving --- grid_legends/grid_legends.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/grid_legends/grid_legends.py b/grid_legends/grid_legends.py index e451f6b..505b4ec 100644 --- a/grid_legends/grid_legends.py +++ b/grid_legends/grid_legends.py @@ -21,20 +21,21 @@ from harness_utils.output import ( from harness_utils.process import terminate_processes from harness_utils.keras_service import KerasService from harness_utils.steam import exec_steam_game +from harness_utils.artifacts import ArtifactManager, ArtifactType SCRIPT_DIR = Path(__file__).resolve().parent LOG_DIR = SCRIPT_DIR.joinpath("run") PROCESS_NAME = "gridlegends.exe" STEAM_GAME_ID = 1307710 +username = os.getlogin() +config_path = f"C:\\Users\\{username}\\Documents\\My Games\\GRID Legends\\hardwaresettings" +config_filename = "hardware_settings_config.xml" +cfg = f"{config_path}\\{config_filename}" def get_resolution() -> tuple[int]: """Gets resolution width and height from local xml file created by game.""" - username = os.getlogin() - config_path = f"C:\\Users\\{username}\\Documents\\My Games\\GRID Legends\\hardwaresettings" - config_filename = "hardware_settings_config.xml" resolution = re.compile(r" Date: Fri, 27 Sep 2024 13:53:44 -0700 Subject: [PATCH 10/21] Artifact manager implementation for cs2, added menu navigation using pyautogui --- counterstrike2/cs2.py | 66 ++++++++++++++++++- counterstrike2/screenshots/settings_1080.png | Bin 0 -> 2073 bytes counterstrike2/screenshots/settings_1440.png | Bin 0 -> 2312 bytes counterstrike2/screenshots/settings_2160.png | Bin 0 -> 4878 bytes 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 counterstrike2/screenshots/settings_1080.png create mode 100644 counterstrike2/screenshots/settings_1440.png create mode 100644 counterstrike2/screenshots/settings_2160.png diff --git a/counterstrike2/cs2.py b/counterstrike2/cs2.py index b9df156..4be4cdc 100644 --- a/counterstrike2/cs2.py +++ b/counterstrike2/cs2.py @@ -19,7 +19,8 @@ from harness_utils.output import ( DEFAULT_DATE_FORMAT) from harness_utils.process import terminate_processes from harness_utils.keras_service import KerasService -from harness_utils.steam import exec_steam_game +from harness_utils.steam import exec_steam_game, get_registry_active_user, get_steam_folder_path +from harness_utils.artifacts import ArtifactManager, ArtifactType SCRIPT_DIR = Path(__file__).resolve().parent @@ -27,6 +28,9 @@ LOG_DIR = SCRIPT_DIR.joinpath("run") PROCESS_NAME = "cs2.exe" STEAM_GAME_ID = 730 +STEAM_USER_ID = get_registry_active_user() +cfg = Path(get_steam_folder_path(), "userdata", str(STEAM_USER_ID), str(STEAM_GAME_ID), "local", "cfg", "cs2_video.txt") + def setup_logging(): """default logging config""" LOG_DIR.mkdir(exist_ok=True) @@ -56,6 +60,8 @@ def run_benchmark(keras_service): copy_config() setup_start_time = time.time() start_game() + + am = ArtifactManager(LOG_DIR) time.sleep(20) # wait for game to load into main menu result = keras_service.wait_for_word("play", timeout=30, interval=0.1) @@ -63,6 +69,57 @@ def run_benchmark(keras_service): logging.info("Did not find the play menu. Did the game load?") sys.exit(1) + height, width = get_resolution() + location = None + + # We check the resolution so we know which screenshot to use for the locate on screen function + match width: + case "1920": + location = gui.locateOnScreen("C:\markbench-2.1.2-2f39666\harness\LTTLabsOSS-markbench-tests\counterstrike2_2\screenshots\settings_1080.png") + case "2560": + location = gui.locateOnScreen("C:\markbench-2.1.2-2f39666\harness\LTTLabsOSS-markbench-tests\counterstrike2_2\screenshots\settings_1440.png") + case "3840": + location = gui.locateOnScreen("C:\markbench-2.1.2-2f39666\harness\LTTLabsOSS-markbench-tests\counterstrike2_2\screenshots\settings_2160.png") + case _: + logging.error("Could not find the settings cog. The game resolution is currently " + height + "," + width + ". Are you using a standard resolution?") + sys.exit(1) + + click_me = gui.center(location) + gui.click(click_me.x, click_me.y) + time.sleep(0.2) + + if keras_service.wait_for_word(word="brightness", timeout=30, interval=1) is None: + logging.info("Did not find the video settings menu. Did the menu get stuck?") + sys.exit(1) + + am.take_screenshot("video.png", ArtifactType.CONFIG_IMAGE, "picture of video settings") + + result = keras_service.look_for_word(word="advanced", attempts=10, interval=1) + if not result: + logging.info("Did not find the advanced video menu. Did Keras click correctly?") + sys.exit(1) + + gui.click(result["x"], result["y"]) + time.sleep(0.2) + + am.take_screenshot("advanced_video_1.png", ArtifactType.CONFIG_IMAGE, "first picture of advanced video settings") + + result = keras_service.look_for_word(word="boost", attempts=10, interval=1) + if not result: + logging.info("Did not find the keyword 'Boost' in the advanced video menu. Did Keras click correctly?") + sys.exit(1) + + gui.moveTo(result["x"], result["y"]) + time.sleep(1) + gui.scroll(-6000000) + time.sleep(1) + + if keras_service.wait_for_word(word="particle", timeout=30, interval=1) is None: + logging.info("Did not find the keyword 'Particle' in advanced video menu. Did Keras scroll correctly?") + sys.exit(1) + + am.take_screenshot("advanced_video_2.png", ArtifactType.CONFIG_IMAGE, "second picture of advanced video settings") + logging.info('Starting benchmark') user.press("`") time.sleep(0.5) @@ -107,9 +164,14 @@ def run_benchmark(keras_service): test_end_time = time.time() logging.info("The console opened. Marking end time.") - time.sleep(10) + time.sleep(8) + + am.take_screenshot("result.png", ArtifactType.RESULTS_IMAGE, "benchmark results") + am.copy_file(Path(cfg), ArtifactType.CONFIG_TEXT, "cs2 video config") logging.info("Run completed. Closing game.") + time.sleep(2) + elapsed_test_time = round((test_end_time - test_start_time), 2) logging.info("Benchmark took %f seconds", elapsed_test_time) terminate_processes(PROCESS_NAME) diff --git a/counterstrike2/screenshots/settings_1080.png b/counterstrike2/screenshots/settings_1080.png new file mode 100644 index 0000000000000000000000000000000000000000..1da95e3d8230c55b98c124cc75ca725df701d09c GIT binary patch literal 2073 zcmV+!2RP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2d+s(K~!i%tyx`c z6jc;HyF0TjE%Ik5L? zGD!(`C@S!f$z+0XL;(VWF>p^Dz^5?3*z-cisDfyuQgBE;csaq`;J=khm9&);%o6@% zru}W6BoP@E$>@Oda2$rC02E8{7*YEJG6o#vae|O^vN<-Kay!9zG{KXb^d`wTa74|N zsof+=?#*}Izlth$JA~TnU0R>=_^4lieNpL<;nBcK2ui2T)pgaO==sfm(( znh5W~><`=*bThS^B*&CC=^^F(oY(O#Y%wSL90L9sDbDUg3Bd{%QxB}rn zCAuKVx|^F9g=5FRrw>0pD3L^!8MTtFPFrH%KL$LO&|y24 zsXB(C6n*xWx+S;{Z$G zq;aE*QmVEJ&Zga~0w&efkh93DXu@ZZF!dN zzxy^CWf5KK?W0Xkt)Pk5PGIMXBayakYQC|a+B<%ymYW-CFe$96jT)6dk`Kj3OPlA7bdtv5B%4O8pf ztLR9yKJ&5`d5a@IBGC&2mnrnd#?>@q+EiClKwn=!eYAH!eRHxSKh+jR&cmSqx*Gi!Pw$?s{ogO<#zH}>^Jd@}fzFAuO?BbQa+l&+j$S$y*0`)J*&<-B^- z;4Z<0@ndQ6+?mFEz}+SD8;bgmuC1Z49uR^y$GWAwV@hFEZy*G4o_TVrKD&=tr8W@^;2lm z+Q)cM^QadtUXldHz_Vvfr?F$Maa)RkplyA32c7KfaW%z|$`w_mw*O#HlE|k~JD`Cb zhZ|9)?YC}f`=OoB3cw&9#*Z6Ai<%p0(SpVz$NifiY@A}pO=HOH@x#f>xTXicdG_&A z%mo==#B*KAn>#*}VyvIn`u$%XrY+k(psq7LffW4f>^XYv?Va@b!LMjw@G|xCZVVeH z+Yo#%!Jfh^Y+i?#8JJJ3cy4PB7jZsw`!S<#9CiQOD>i`IDvRFPvc9AYz~;2!_3g4S zT+TTBH%F7XoW{LDFum-gnc`ByOBu`!l{yZbz0g;bD+5Cg4Gm@7s{;N$e_mc*&==#O zWGLr2fLMbfI=qD-31bp3C@K&nE9fTnT^+9gQfOr|!$s0_?w{C8GHgM-YrL`HUW}Po zws9-d)AwjGAm3rwAf{EfWAUz%#(Jt@n7yv9t`f@NSC25rdo>?eWjx!U3{K_aEgl0M zDm~;&0POngWOGRx%<_x2CMnBi%BWXPy>HNAbFvCF+l4O=SD`RN)Zx3S%`f*N*LPxBER#p^XTsB@HHB}cZ#1a-($YCE4XV4c@6awT1dim`t zeet)OiX#tB`XgZyFY3eC-VN=3v9KcX4Su_*--Se)t|HFc9w%8$?VjL`3V9Kg>o!vZ zcGW&m&?8@Rh=5Pez4gm*@jjB`_gl7A(ymLj?XECti76}iwFK3z$*!GLE01E5c8_)* zD)>Px&tI4`^zp>9*clekC9E$5{e$e{>w(X27NY+Fr!X7N{J3vU00000NkvXXu0mjf DA<5?^ literal 0 HcmV?d00001 diff --git a/counterstrike2/screenshots/settings_1440.png b/counterstrike2/screenshots/settings_1440.png new file mode 100644 index 0000000000000000000000000000000000000000..6cf816be54ed2b5451d38b8897cd7749402b352f GIT binary patch literal 2312 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 Date: Tue, 1 Oct 2024 11:14:23 -0700 Subject: [PATCH 11/21] returnal artifact manager implementation --- returnal/returnal.py | 66 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/returnal/returnal.py b/returnal/returnal.py index 26030f5..80a00ef 100644 --- a/returnal/returnal.py +++ b/returnal/returnal.py @@ -24,6 +24,7 @@ from harness_utils.steam import ( exec_steam_run_command, get_steamapps_common_path, ) +from harness_utils.artifacts import ArtifactManager, ArtifactType STEAM_GAME_ID = 1649240 SCRIPT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) @@ -90,6 +91,7 @@ def run_benchmark() -> tuple[float]: logging.info("Starting game") exec_steam_run_command(STEAM_GAME_ID) setup_start_time = time.time() + am = ArtifactManager(LOG_DIRECTORY) time.sleep(10) @@ -105,8 +107,63 @@ def run_benchmark() -> tuple[float]: logging.info("Could not find prompt to open menu!") sys.exit(1) - # Navigate to in-game benchmark and start it - navigate_options_menu() + # Navigate to display menu + user.press("esc") + time.sleep(1) + user.press("enter") + time.sleep(1) + user.press("q") + time.sleep(1) + user.press("q") + time.sleep(1) + + if kerasService.wait_for_word(word="aspect", timeout=30, interval=1) is None: + logging.info("Did not find the video settings menu. Did the menu get stuck?") + sys.exit(1) + am.take_screenshot("video.png", ArtifactType.CONFIG_IMAGE, "picture of video settings") + + # Navigate to graphics menu + user.press("e") + time.sleep(1) + + if kerasService.wait_for_word(word="vsync", timeout=30, interval=1) is None: + logging.info("Did not find the graphics settings menu. Did the menu get stuck?") + sys.exit(1) + am.take_screenshot("graphics_1.png", ArtifactType.CONFIG_IMAGE, "first picture of graphics settings") + + # We check for a keyword that indicates DLSS is active because this changes how we navigate the menu + if kerasService.wait_for_word(word="sharpness", timeout=10, interval=1) is None: + logging.info("No DLSS Settings Detected") + # Scroll down graphics menu + for i in range(15): + user.press("down") + time.sleep(0.2) + else: + logging.info("DLSS Settings Detected") + # Scroll down graphics menu + for i in range(17): + user.press("down") + time.sleep(0.2) + + if kerasService.wait_for_word(word="volumetric", timeout=30, interval=1) is None: + logging.info("Did not find the keyword 'volumetric'. Did the the menu scroll correctly?") + sys.exit(1) + am.take_screenshot("graphics_2.png", ArtifactType.CONFIG_IMAGE, "second picture of graphics settings") + + # Scroll down graphics menu + for i in range(15): + user.press("down") + time.sleep(0.2) + + if kerasService.wait_for_word(word="hdr", timeout=30, interval=1) is None: + logging.info("Did not find the keyword 'hdr'. Did the the menu scroll correctly?") + sys.exit(1) + am.take_screenshot("graphics_3.png", ArtifactType.CONFIG_IMAGE, "third picture of graphics settings") + + # Launch the benchmark + user.keyDown("tab") + time.sleep(5) + user.keyUp("tab") setup_end_time = time.time() elapsed_setup_time = round((setup_end_time - setup_start_time), 2) @@ -136,6 +193,11 @@ def run_benchmark() -> tuple[float]: logging.info( "Results screen was not found! Did harness not wait long enough? Or test was too long?") sys.exit(1) + + # Give results screen time to fill out, then save screenshot and config file + time.sleep(2) + am.take_screenshot("result.png", ArtifactType.RESULTS_IMAGE, "screenshot of benchmark result") + am.copy_file(LOCAL_USER_SETTINGS, ArtifactType.CONFIG_TEXT, "config file") elapsed_test_time = round((test_end_time - test_start_time), 2) logging.info("Benchmark took %s seconds", elapsed_test_time) From 2c494f1d8f350a5cb6ab8caa85364fde9c4247c3 Mon Sep 17 00:00:00 2001 From: Arty Blue Date: Thu, 3 Oct 2024 12:35:11 -0700 Subject: [PATCH 12/21] hopefully linty is happy now, also updated paths to avoid hardcoded absolute paths --- counterstrike2/cs2.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/counterstrike2/cs2.py b/counterstrike2/cs2.py index 4be4cdc..14ed907 100644 --- a/counterstrike2/cs2.py +++ b/counterstrike2/cs2.py @@ -60,7 +60,6 @@ def run_benchmark(keras_service): copy_config() setup_start_time = time.time() start_game() - am = ArtifactManager(LOG_DIR) time.sleep(20) # wait for game to load into main menu @@ -75,17 +74,20 @@ def run_benchmark(keras_service): # We check the resolution so we know which screenshot to use for the locate on screen function match width: case "1920": - location = gui.locateOnScreen("C:\markbench-2.1.2-2f39666\harness\LTTLabsOSS-markbench-tests\counterstrike2_2\screenshots\settings_1080.png") + location = gui.locateOnScreen(f"{SCRIPT_DIR}\\screenshots\\settings_1080.png") case "2560": - location = gui.locateOnScreen("C:\markbench-2.1.2-2f39666\harness\LTTLabsOSS-markbench-tests\counterstrike2_2\screenshots\settings_1440.png") + location = gui.locateOnScreen(f"{SCRIPT_DIR}\\screenshots\\settings_1440.png") case "3840": - location = gui.locateOnScreen("C:\markbench-2.1.2-2f39666\harness\LTTLabsOSS-markbench-tests\counterstrike2_2\screenshots\settings_2160.png") + location = gui.locateOnScreen(f"{SCRIPT_DIR}\\screenshots\\settings_2160.png") case _: - logging.error("Could not find the settings cog. The game resolution is currently " + height + "," + width + ". Are you using a standard resolution?") + logging.error("Could not find the settings cog. The game resolution is currently %s, %s. Are you using a standard resolution?", height, width) sys.exit(1) click_me = gui.center(location) - gui.click(click_me.x, click_me.y) + gui.moveTo(click_me.x, click_me.y) + gui.mouseDown() + time.sleep(0.2) + gui.mouseUp() time.sleep(0.2) if keras_service.wait_for_word(word="brightness", timeout=30, interval=1) is None: @@ -99,7 +101,10 @@ def run_benchmark(keras_service): logging.info("Did not find the advanced video menu. Did Keras click correctly?") sys.exit(1) - gui.click(result["x"], result["y"]) + gui.moveTo(result["x"], result["y"]) + gui.mouseDown() + time.sleep(0.2) + gui.mouseUp() time.sleep(0.2) am.take_screenshot("advanced_video_1.png", ArtifactType.CONFIG_IMAGE, "first picture of advanced video settings") @@ -117,8 +122,7 @@ def run_benchmark(keras_service): if keras_service.wait_for_word(word="particle", timeout=30, interval=1) is None: logging.info("Did not find the keyword 'Particle' in advanced video menu. Did Keras scroll correctly?") sys.exit(1) - - am.take_screenshot("advanced_video_2.png", ArtifactType.CONFIG_IMAGE, "second picture of advanced video settings") + am.take_screenshot("advanced_video_2.png", ArtifactType.CONFIG_IMAGE, "second picture of advanced video settings") logging.info('Starting benchmark') user.press("`") @@ -164,12 +168,12 @@ def run_benchmark(keras_service): test_end_time = time.time() logging.info("The console opened. Marking end time.") + # allow time for result screen to populate time.sleep(8) - + am.take_screenshot("result.png", ArtifactType.RESULTS_IMAGE, "benchmark results") am.copy_file(Path(cfg), ArtifactType.CONFIG_TEXT, "cs2 video config") logging.info("Run completed. Closing game.") - time.sleep(2) elapsed_test_time = round((test_end_time - test_start_time), 2) From 185cda9ce00c92cc79baab04e04a2065f2e01d37 Mon Sep 17 00:00:00 2001 From: Arty Blue Date: Thu, 3 Oct 2024 12:54:43 -0700 Subject: [PATCH 13/21] added implementation of steam build ID tracking --- counterstrike2/cs2.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/counterstrike2/cs2.py b/counterstrike2/cs2.py index 14ed907..2327e27 100644 --- a/counterstrike2/cs2.py +++ b/counterstrike2/cs2.py @@ -19,7 +19,7 @@ from harness_utils.output import ( DEFAULT_DATE_FORMAT) from harness_utils.process import terminate_processes from harness_utils.keras_service import KerasService -from harness_utils.steam import exec_steam_game, get_registry_active_user, get_steam_folder_path +from harness_utils.steam import exec_steam_game, get_registry_active_user, get_steam_folder_path, get_build_id from harness_utils.artifacts import ArtifactManager, ArtifactType @@ -200,7 +200,8 @@ def main(): report = { "resolution": format_resolution(width, height), "start_time": seconds_to_milliseconds(start_time), - "end_time": seconds_to_milliseconds(end_time) + "end_time": seconds_to_milliseconds(end_time), + "version": get_build_id(STEAM_GAME_ID) } write_report_json(LOG_DIR, "report.json", report) From f917f38de6d801647d3fb77816c1df64247ffb1f Mon Sep 17 00:00:00 2001 From: Arty Blue Date: Thu, 3 Oct 2024 15:06:46 -0700 Subject: [PATCH 14/21] fixed linter issues and implemented steam build ID feature --- total_war_warhammer_iii/twwh3.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/total_war_warhammer_iii/twwh3.py b/total_war_warhammer_iii/twwh3.py index da1264a..59dc7bc 100644 --- a/total_war_warhammer_iii/twwh3.py +++ b/total_war_warhammer_iii/twwh3.py @@ -20,7 +20,7 @@ from harness_utils.output import ( DEFAULT_LOGGING_FORMAT, DEFAULT_DATE_FORMAT ) -from harness_utils.steam import get_app_install_location +from harness_utils.steam import get_app_install_location, get_build_id from harness_utils.keras_service import KerasService from harness_utils.artifacts import ArtifactManager, ArtifactType @@ -32,8 +32,7 @@ STEAM_GAME_ID = 1142710 APPDATA = os.getenv("APPDATA") CONFIG_LOCATION = f"{APPDATA}\\The Creative Assembly\\Warhammer3\\scripts" CONFIG_FILENAME = "preferences.script.txt" - -cfg = f"{CONFIG_LOCATION}\\{CONFIG_FILENAME}" +CONFIG_FULL_PATH = f"{CONFIG_LOCATION}\\{CONFIG_FILENAME}" user.FAILSAFE = False @@ -71,7 +70,6 @@ def run_benchmark(): start_game() setup_start_time = time.time() time.sleep(5) - am = ArtifactManager(LOG_DIRECTORY) result = kerasService.look_for_word("warning", attempts=10, interval=5) @@ -159,7 +157,7 @@ def run_benchmark(): time.sleep(5) am.take_screenshot("result.png", ArtifactType.RESULTS_IMAGE, "benchmark results") - am.copy_file(Path(cfg), ArtifactType.RESULTS_TEXT, "preferences.script.txt") + am.copy_file(Path(CONFIG_FULL_PATH), ArtifactType.RESULTS_TEXT, "preferences.script.txt") # End the run elapsed_test_time = round(test_end_time - test_start_time, 2) @@ -198,7 +196,8 @@ try: report = { "resolution": format_resolution(width, height), "start_time": seconds_to_milliseconds(start_time), - "end_time": seconds_to_milliseconds(endtime) + "end_time": seconds_to_milliseconds(endtime), + "version": get_build_id(STEAM_GAME_ID) } write_report_json(LOG_DIRECTORY, "report.json", report) From 951ca63cc99acd959777ffa3382d5430a43e5616 Mon Sep 17 00:00:00 2001 From: Arty Blue Date: Thu, 3 Oct 2024 15:36:39 -0700 Subject: [PATCH 15/21] modifications to make the linter happier with my work --- grid_legends/grid_legends.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/grid_legends/grid_legends.py b/grid_legends/grid_legends.py index 505b4ec..c770907 100644 --- a/grid_legends/grid_legends.py +++ b/grid_legends/grid_legends.py @@ -29,16 +29,16 @@ PROCESS_NAME = "gridlegends.exe" STEAM_GAME_ID = 1307710 username = os.getlogin() -config_path = f"C:\\Users\\{username}\\Documents\\My Games\\GRID Legends\\hardwaresettings" -config_filename = "hardware_settings_config.xml" -cfg = f"{config_path}\\{config_filename}" +CONFIG_PATH = f"C:\\Users\\{username}\\Documents\\My Games\\GRID Legends\\hardwaresettings" +CONFIG_FILENAME = "hardware_settings_config.xml" +CONFIG_FULL_PATH = f"{CONFIG_PATH}\\{CONFIG_FILENAME}" def get_resolution() -> tuple[int]: """Gets resolution width and height from local xml file created by game.""" resolution = re.compile(r" Date: Thu, 3 Oct 2024 15:41:58 -0700 Subject: [PATCH 16/21] added steam build ID --- grid_legends/grid_legends.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grid_legends/grid_legends.py b/grid_legends/grid_legends.py index c770907..a71a0fb 100644 --- a/grid_legends/grid_legends.py +++ b/grid_legends/grid_legends.py @@ -20,7 +20,7 @@ from harness_utils.output import ( DEFAULT_DATE_FORMAT) from harness_utils.process import terminate_processes from harness_utils.keras_service import KerasService -from harness_utils.steam import exec_steam_game +from harness_utils.steam import exec_steam_game, get_build_id from harness_utils.artifacts import ArtifactManager, ArtifactType SCRIPT_DIR = Path(__file__).resolve().parent @@ -174,7 +174,8 @@ def main(): report = { "resolution": format_resolution(width, height), "start_time": seconds_to_milliseconds(start_time), - "end_time": seconds_to_milliseconds(end_time) + "end_time": seconds_to_milliseconds(end_time), + "version": get_build_id(STEAM_GAME_ID) } write_report_json(LOG_DIR, "report.json", report) From 95b8736811c67d0b50f4a5c934b58f8f63e80968 Mon Sep 17 00:00:00 2001 From: Arty Blue Date: Thu, 3 Oct 2024 16:25:24 -0700 Subject: [PATCH 17/21] added the steam build ID reporting feature, fixed issues with linter --- returnal/returnal.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/returnal/returnal.py b/returnal/returnal.py index 80a00ef..0205336 100644 --- a/returnal/returnal.py +++ b/returnal/returnal.py @@ -18,11 +18,12 @@ from harness_utils.output import ( DEFAULT_LOGGING_FORMAT, DEFAULT_DATE_FORMAT, ) -from harness_utils.misc import remove_files +from harness_utils.misc import remove_files, press_n_times from harness_utils.process import terminate_processes from harness_utils.steam import ( exec_steam_run_command, get_steamapps_common_path, + get_build_id ) from harness_utils.artifacts import ArtifactManager, ArtifactType @@ -116,7 +117,8 @@ def run_benchmark() -> tuple[float]: time.sleep(1) user.press("q") time.sleep(1) - + + # Verify that we have navigated to the video settings menu and take a screenshot if kerasService.wait_for_word(word="aspect", timeout=30, interval=1) is None: logging.info("Did not find the video settings menu. Did the menu get stuck?") sys.exit(1) @@ -135,15 +137,11 @@ def run_benchmark() -> tuple[float]: if kerasService.wait_for_word(word="sharpness", timeout=10, interval=1) is None: logging.info("No DLSS Settings Detected") # Scroll down graphics menu - for i in range(15): - user.press("down") - time.sleep(0.2) + press_n_times("down", 15, 0.2) else: logging.info("DLSS Settings Detected") # Scroll down graphics menu - for i in range(17): - user.press("down") - time.sleep(0.2) + press_n_times("down", 17, 0.2) if kerasService.wait_for_word(word="volumetric", timeout=30, interval=1) is None: logging.info("Did not find the keyword 'volumetric'. Did the the menu scroll correctly?") @@ -151,9 +149,7 @@ def run_benchmark() -> tuple[float]: am.take_screenshot("graphics_2.png", ArtifactType.CONFIG_IMAGE, "second picture of graphics settings") # Scroll down graphics menu - for i in range(15): - user.press("down") - time.sleep(0.2) + press_n_times("down", 15, 0.2) if kerasService.wait_for_word(word="hdr", timeout=30, interval=1) is None: logging.info("Did not find the keyword 'hdr'. Did the the menu scroll correctly?") @@ -193,8 +189,8 @@ def run_benchmark() -> tuple[float]: logging.info( "Results screen was not found! Did harness not wait long enough? Or test was too long?") sys.exit(1) - - # Give results screen time to fill out, then save screenshot and config file + + # Give results screen time to fill out, then save screenshot and config file time.sleep(2) am.take_screenshot("result.png", ArtifactType.RESULTS_IMAGE, "screenshot of benchmark result") am.copy_file(LOCAL_USER_SETTINGS, ArtifactType.CONFIG_TEXT, "config file") @@ -227,7 +223,8 @@ try: report = { "resolution": format_resolution(width, height), "start_time": seconds_to_milliseconds(start_time), - "end_time": seconds_to_milliseconds(end_time) + "end_time": seconds_to_milliseconds(end_time), + "version": get_build_id(STEAM_GAME_ID) } write_report_json(LOG_DIRECTORY, "report.json", report) From 86ccbfe78bc267c01c003b9d1694d17ed63fac86 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Fri, 4 Oct 2024 09:35:54 -0700 Subject: [PATCH 18/21] small fixes --- harness_utils/keras_service.py | 6 ++-- .../shadowofthetombraider.py | 32 ++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/harness_utils/keras_service.py b/harness_utils/keras_service.py index 71ce1a4..69b63c8 100644 --- a/harness_utils/keras_service.py +++ b/harness_utils/keras_service.py @@ -101,7 +101,7 @@ class KerasService(): def _divide_horizontal(self, screenshot, quadrant: ScreenShotQuadrant): """divide the screenshot horizontally""" - height, _, _ = screenshot.shape + height, _ = screenshot.shape if quadrant == ScreenShotQuadrant.TOP: return screenshot[0:int(height/2), :] if quadrant == ScreenShotQuadrant.BOTTOM: @@ -111,7 +111,7 @@ class KerasService(): def _divide_vertical(self, screenshot, quadrant: ScreenShotQuadrant): """divide the screenshot vertically""" - _, width, _ = screenshot.shape + _, width = screenshot.shape if quadrant == quadrant.LEFT: return screenshot[:, 0:int(width/2)] if quadrant == quadrant.RIGHT: @@ -121,7 +121,7 @@ class KerasService(): def _divide_in_four(self, screenshot, quadrant: ScreenShotQuadrant): """divide the screenshot in four quadrants""" - height, width, _ = screenshot.shape + height, width = screenshot.shape if quadrant == ScreenShotQuadrant.TOP_LEFT: return screenshot[0:int(height/2), 0:int(width/2)] if quadrant == ScreenShotQuadrant.TOP_RIGHT: diff --git a/shadowofthetombraider/shadowofthetombraider.py b/shadowofthetombraider/shadowofthetombraider.py index d72ff94..79c2f6c 100644 --- a/shadowofthetombraider/shadowofthetombraider.py +++ b/shadowofthetombraider/shadowofthetombraider.py @@ -18,7 +18,7 @@ from harness_utils.output import ( DEFAULT_LOGGING_FORMAT, DEFAULT_DATE_FORMAT) from harness_utils.process import terminate_processes -from harness_utils.keras_service import KerasService +from harness_utils.keras_service import KerasService, ScreenShotDivideMethod, ScreenShotQuadrant, ScreenSplitConfig from harness_utils.steam import exec_steam_game from harness_utils.artifacts import ArtifactManager, ArtifactType @@ -48,23 +48,31 @@ def start_game(): def run_benchmark(): """Start game via Steam and enter fullscreen mode""" - setup_start_time = time.time() - start_game() - args = get_args() keras_service = KerasService(args.keras_host, args.keras_port) am = ArtifactManager(LOG_DIR) - if keras_service.wait_for_word(word="options", timeout=30, interval=1) is None: + setup_start_time = time.time() + start_game() + time.sleep(10) + + ss_config = ScreenSplitConfig( + divide_method=ScreenShotDivideMethod.QUADRANT, + quadrant=ScreenShotQuadrant.BOTTOM_RIGHT + ) + + if keras_service.wait_for_word(word="options", timeout=30, interval=1, split_config=ss_config) is None: logging.info("Did not find the options menu. Did the game launch correctly?") sys.exit(1) + logging.info("found options") + user.press("up") - time.sleep(0.2) + time.sleep(0.5) user.press("up") - time.sleep(0.2) + time.sleep(0.5) user.press("up") - time.sleep(0.2) + time.sleep(0.5) user.press("enter") time.sleep(1) @@ -72,12 +80,14 @@ def run_benchmark(): logging.info("Did not find the graphics menu. Did the menu get stuck?") sys.exit(1) + logging.info("found graphics") + user.press("down") - time.sleep(0.2) + time.sleep(0.5) user.press("down") - time.sleep(0.2) + time.sleep(0.5) user.press("down") - time.sleep(0.2) + time.sleep(0.5) user.press("enter") time.sleep(1) From 7d3e97d094739433457ec33875fb5589694e25cb Mon Sep 17 00:00:00 2001 From: Nikolas Date: Fri, 4 Oct 2024 09:38:26 -0700 Subject: [PATCH 19/21] fix sottr --- shadowofthetombraider/shadowofthetombraider.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowofthetombraider/shadowofthetombraider.py b/shadowofthetombraider/shadowofthetombraider.py index 79c2f6c..26296ee 100644 --- a/shadowofthetombraider/shadowofthetombraider.py +++ b/shadowofthetombraider/shadowofthetombraider.py @@ -57,8 +57,8 @@ def run_benchmark(): time.sleep(10) ss_config = ScreenSplitConfig( - divide_method=ScreenShotDivideMethod.QUADRANT, - quadrant=ScreenShotQuadrant.BOTTOM_RIGHT + divide_method=ScreenShotDivideMethod.HORIZONTAL, + quadrant=ScreenShotQuadrant.TOP ) if keras_service.wait_for_word(word="options", timeout=30, interval=1, split_config=ss_config) is None: From e175278a57739ede4f66edbf982d201f4d960d3f Mon Sep 17 00:00:00 2001 From: Nikolas Date: Fri, 4 Oct 2024 09:42:04 -0700 Subject: [PATCH 20/21] fix lint --- shadowofthetombraider/shadowofthetombraider.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/shadowofthetombraider/shadowofthetombraider.py b/shadowofthetombraider/shadowofthetombraider.py index 26296ee..cde8f12 100644 --- a/shadowofthetombraider/shadowofthetombraider.py +++ b/shadowofthetombraider/shadowofthetombraider.py @@ -46,12 +46,8 @@ def start_game(): return exec_steam_game(STEAM_GAME_ID, game_params=["-nolauncher"]) -def run_benchmark(): +def run_benchmark(keras_service, am): """Start game via Steam and enter fullscreen mode""" - args = get_args() - keras_service = KerasService(args.keras_host, args.keras_port) - am = ArtifactManager(LOG_DIR) - setup_start_time = time.time() start_game() time.sleep(10) @@ -156,7 +152,12 @@ def run_benchmark(): if __name__ == "__main__": try: setup_logging() - run_benchmark() + + args = get_args() + keras_service = KerasService(args.keras_host, args.keras_port) + am = ArtifactManager(LOG_DIR) + + run_benchmark(keras_service, am) except Exception as ex: logging.error("Something went wrong running the benchmark!") logging.exception(ex) From d7b9551c7993d6223124ab5fb459dd26271aae9d Mon Sep 17 00:00:00 2001 From: Nikolas Date: Fri, 4 Oct 2024 09:53:17 -0700 Subject: [PATCH 21/21] linter please --- shadowofthetombraider/shadowofthetombraider.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/shadowofthetombraider/shadowofthetombraider.py b/shadowofthetombraider/shadowofthetombraider.py index cde8f12..4672bc4 100644 --- a/shadowofthetombraider/shadowofthetombraider.py +++ b/shadowofthetombraider/shadowofthetombraider.py @@ -149,15 +149,18 @@ def run_benchmark(keras_service, am): write_report_json(LOG_DIR, "report.json", report) +def main(): + """entry point""" + setup_logging() + args = get_args() + keras_service = KerasService(args.keras_host, args.keras_port) + am = ArtifactManager(LOG_DIR) + run_benchmark(keras_service, am) + + if __name__ == "__main__": try: - setup_logging() - - args = get_args() - keras_service = KerasService(args.keras_host, args.keras_port) - am = ArtifactManager(LOG_DIR) - - run_benchmark(keras_service, am) + main() except Exception as ex: logging.error("Something went wrong running the benchmark!") logging.exception(ex)