Example #1
0
    def get_ui_element(self, element, screenshot=None, exit_on_fail=True):
        """
            Find a UI element on the screen
            """
        # Move the mouse so it doesn't obstruct search
        pyautogui.moveTo(400, 400)

        # Get a screenshot
        if screenshot is None:
            screenshot = utils.take_screenshot(False)

        # Try to match the template in the screenshot
        result = cv2.matchTemplate(screenshot, self.templates[element],
                                   cv2.TM_CCORR_NORMED)
        _, max_val, _, max_loc = cv2.minMaxLoc(result)

        # Found the element, return the location
        if max_val > 0.9:
            return max_loc

        # Failed to find the element with high enough confidence
        if not exit_on_fail:
            return False

        # Not finding the element is severe enough to quit the game
        utils.log("SEVERE",
                  F"Failed to find {element}, max confidence was {max_val}")
        utils.quit_game()
Example #2
0
    def step(self, new_position):
        """
            Given a set of coordinates, calculate what direction to step
            and send that key to the game
            """
        # Calculate direction of movement
        x_diff = self.game_map.player_position[0] - new_position[0]
        y_diff = self.game_map.player_position[1] - new_position[1]

        # Determine what key to press
        direction = ''
        if x_diff == 1:
            direction = 'a'
        elif x_diff == -1:
            direction = 'd'
        elif y_diff == 1:
            direction = 'w'
        elif y_diff == -1:
            direction = 's'
        else:
            utils.log(
                'SEVERE',
                F"Invalid step difference. xDiff: {x_diff}, yDiff: {y_diff}")
            utils.quit_game()

        # Move along path
        pyautogui.press(direction)
        sleep(0.1)

        # Player moved, re-detect environment
        screenshot = utils.take_screenshot()
        self.game_map.update_player_position(screenshot)
        self.game_map.update_map()
Example #3
0
    def get_weight(self):
        """
            Gets the player's current and max weight
            """
        # Find the current and total weight
        screenshot = utils.take_screenshot(False)
        weight = self.get_ui_element('weight', screenshot)
        weight = screenshot[weight[1]:(weight[1] + 12),
                            (weight[0] + 40):(weight[0] + 84)]

        # Resize and parse the image to a string
        weight = cv2.resize(weight, (0, 0), fx=3, fy=3)
        weight = cv2.cvtColor(weight, cv2.COLOR_BGR2GRAY)
        weight = cv2.bitwise_not(weight)
        weight = cv2.fastNlMeansDenoising(weight, None, 9, 13)
        _, weight = cv2.threshold(weight, 180, 255, cv2.THRESH_BINARY)
        weight = cv2.blur(weight, (4, 2))

        weight_text = ''
        try:
            weight_text = pytesseract.image_to_string(
                weight, config=utils.TESSERACT_CONF)
        except UnicodeDecodeError:
            utils.log(
                "SEVERE",
                "Tesseract failed to parse player weight from screenshot")
            utils.quit_game()

        # Split the equation and calculate the difference
        current_weight, max_weight = weight_text.split("/")[0::1]
        return int(current_weight), int(max_weight)
Example #4
0
    def mine(self):
        """
            Mine ore until weight is nearing full.  If player destroys all pickaxes,
            another will be purchased and mining will continue.
            """
        # Get player's weight
        if self.player.is_weight_below_threshold(50):
            utils.log("INFO", F"Weight is below threshold, switching task to smelting")
            return self.player.TASKS.SMELT

        # No pickaxes left, buy one
        if not self.player.backpack.get_item('pickaxe'):
            self.merchant.buy_item('pickaxe', self.merchant.MERCHANTS.BLACKSMITH)
            self.mining_coords = self.move.go_to_mine()
        
        # First time mining, move to the mining region
        if self.mining_coords is None:
            self.mining_coords = self.move.go_to_mine()

        # Check if there is still ore to mine here
        screenshot = utils.take_screenshot()
        self.resolve_nothing_to_mine(screenshot)
        self.resolve_cannot_mine(screenshot)

        # Use pickaxe
        self.player.backpack.use_item('pickaxe', (self.mining_coords[0], self.mining_coords[1]), (9, 4))

        # Continue mining
        return self.player.TASKS.MINE
Example #5
0
    def get_health(self):
        """
            Gets the player's current health
            """
        # Reduce the screenshot to include only the player's health
        screenshot = utils.take_screenshot(False)
        health = self.get_ui_element('health', screenshot)
        health = screenshot[health[1]:(health[1] + 12),
                            (health[0] + 36):(health[0] + 92)]

        # Resize and parse the image to a string
        health = cv2.resize(health, (0, 0), fx=3, fy=3)
        health = cv2.cvtColor(health, cv2.COLOR_BGR2GRAY)
        health = cv2.bitwise_not(health)
        health = cv2.fastNlMeansDenoising(health, None, 9, 13)
        _, health = cv2.threshold(health, 180, 255, cv2.THRESH_BINARY)
        health = cv2.blur(health, (4, 2))

        health_text = ''
        try:
            health_text = pytesseract.image_to_string(
                health, config=utils.TESSERACT_CONF)
        except UnicodeDecodeError:
            utils.log(
                "SEVERE",
                "Tesseract failed to parse player health from screenshot")
            utils.quit_game()

        # Split the equation and calculate the difference
        current = health_text.split("/")[0]
        return int(current)
Example #6
0
    def fire_smelter(self):
        """
            Check if the forge has gone cold. If so, fires it
            """
        # Get the smelter
        screenshot = utils.take_screenshot()
        forge = screenshot[152:168, 168:184]

        # Check if the cold forge exists
        result = cv2.matchTemplate(forge, self.cold_forge_template,
                                   cv2.TM_CCORR_NORMED)
        max_val = cv2.minMaxLoc(result)[1]

        # Found cold forge, light it and wait
        if max_val > 0.9:
            pyautogui.moveTo(192, 159, 0.15)
            pyautogui.doubleClick()
            sleep(1.5)
Example #7
0
    def get_backpack(self):
        """
            Find and return the player's backpack. Returns a tuple
            containing the backpack and the coordinates
            """
        # Find backpack in screenshot
        screenshot = utils.take_screenshot(False)
        result = cv2.matchTemplate(screenshot, self.backpack_template,
                                   cv2.TM_CCORR_NORMED)
        _, max_val, _, backpack_loc = cv2.minMaxLoc(result)

        # Failed to find the backpack with high confidence
        if max_val < 0.9:
            utils.log("SEVERE", "Unable to find backpack in screenshot")
            utils.quit_game()

        # Restrict screenshot to just include the player's backpack
        #backpack_loc = (backpack_loc[0] + 11, backpack_loc[1] + 33)
        backpack = screenshot[(backpack_loc[1]):(backpack_loc[1] + 144),
                              (backpack_loc[0]):(backpack_loc[0] + 128)]

        return (backpack, backpack_loc)
Example #8
0
# Set defaults
task = Player.TASKS.MINE
if len(sys.argv) > 1:
    task = Player.TASKS[sys.argv[1].upper()]

# Initialize classes
game_map = GameMap()
player = Player(game_map, task)
user_interface = UserInterface()

utils.log("INIT", "====================================================")
utils.log("INIT", "Initializing...")
utils.log("INIT", F"Default task set to {task}")

# Find blocking window in screenshot
screenshot = utils.take_screenshot(False)
result = cv2.matchTemplate(screenshot, user_interface.templates['sponsored'], cv2.TM_CCORR_NORMED)
_, max_val, _, max_loc = cv2.minMaxLoc(result)

# Found the blocking window window with high confidence
if max_val > 0.9:
    click_at = (max_loc[0] + 428, max_loc[1] + 144)
    utils.log("INIT", "Closed blocking window")
    pyautogui.moveTo(click_at[0], click_at[1], 0.15)
    pyautogui.click()
    sleep(5)

# Bring game to foreground
utils.bring_game_to_foreground()

# Detect environment
def take_screenshot():
    utilities.take_screenshot(screen)
Example #10
0
    def update_player_position(self, screenshot):
        """To update the player position the following steps are taken.
            1. The sextant is location and used
            2. The coordinates are parsed and normalized
            3. Old player position is removed from map
            4. New player position is added to the map

            If any of these operations fails, the game will quit.

            Returns tuple containing player position as well as the tile map
            """

        sextant_x = None
        sextant_y = None
        errors = 0

        while sextant_x is None:
            # Move mouse to a neutral position that won't obstruct template matching
            pyautogui.moveTo(400, 400)

            # Find and use the sextant
            self.backpack.use_item('sextant', None, (5, 2), True)

            # Take a new screenshot that includes the location
            screenshot = utils.take_screenshot()

            # Find the current position
            position = screenshot[450:465, 120:180]

            # Resize and parse the image to a string
            position = cv2.resize(position, (0, 0), fx=5, fy=5)
            position = cv2.bitwise_not(position)
            position = cv2.blur(position, (8, 8))

            text = ''

            try:
                text = pytesseract.image_to_string(position, config='--psm 8')
                # Split the text into coordinates
                sextant_x, sextant_y = text.split(", ")[0::1]
            except UnicodeDecodeError:
                utils.log("SEVERE",
                          F"Unable to parse sextant coordinates string")
                utils.quit_game()
            except ValueError as value_error:
                utils.log("WARN", F"{value_error}")
                # Move mouse to a neutral position that won't obstruct template matching
                pyautogui.moveTo(400, 400)
                errors += 1

            if errors == 10:
                utils.log("SEVERE", F"Sextant failed 10 times")
                utils.quit_game()

        # Normalize the coordinates to fit the map indicies
        normalized_x = int(sextant_x) - utils.NORMALIZATION_CONSTANT
        normalized_y = int(sextant_y) - utils.NORMALIZATION_CONSTANT

        # Remove old player position from the map
        self.game_map[self.game_map ==
                      self.TILES.PLAYER.value] = self.TILES.ACCESSIBLE.value
        self.player_position = (normalized_x, normalized_y)
        self.game_map[self.player_position] = self.TILES.PLAYER.value
Example #11
0
    def update_map(self, screenshot=None):
        """
            Takes a screenshot of the game, this method will iterate
            over the gameplay region in 16x16 chunks.  Each chunk is compared
            with all potential inaccessible tiles until a match is found.

            When a match is found, the frequency of that tile is incremented
            so future chunks are compared with high frequency tiles first.
            """
        # Get the visible tiles
        nearby = self.game_map[(self.player_position[0] -
                                10):(self.player_position[0] + 11),
                               (self.player_position[1] -
                                10):(self.player_position[1] + 11)]

        # Clear NPCs in the nearby as they may have moved
        nearby[nearby ==
               self.TILES.WEAPON_SHOPKEEPER.value] = self.TILES.UNKNOWN.value
        nearby[nearby ==
               self.TILES.BLACKSMITH.value] = self.TILES.UNKNOWN.value

        # Take screenshot and isolate the gamplay region
        if screenshot is None:
            screenshot = utils.take_screenshot()
        play = screenshot[8:344, 8:344]

        # Loop through all unknown tiles in the nearby
        for i, j in zip(*np.where(nearby == self.TILES.UNKNOWN.value)):
            # Scale up the dimensions
            tile_x = i * self.TILE_DIM
            tile_y = j * self.TILE_DIM

            # The center cell is always the player
            if i == 10 and j == 10:
                tile_x = self.player_position[0] + int(tile_x / 16) - 10
                tile_y = self.player_position[1] + int(tile_y / 16) - 10
                self.game_map[(tile_x, tile_y)] = self.TILES.PLAYER.value
                continue

            # Slice the tile from the play region
            tile = play[tile_y:tile_y + self.TILE_DIM,
                        tile_x:tile_x + self.TILE_DIM]

            tile_x = self.player_position[0] + int(tile_x / 16) - 10
            tile_y = self.player_position[1] + int(tile_y / 16) - 10

            # Go through all tile types looking for a high confidence match
            template = None
            for potential_template in self.templates:
                if np.allclose(potential_template[0], tile, 1, 1):
                    template = potential_template
                    break

            # No match, assume it is inaccessible
            if template is None:
                self.game_map[(tile_x, tile_y)] = self.TILES.INACCESSIBLE.value
                continue

            # By default, mark tile as inaccessible
            label = None

            # Mark as mineable
            if re.search(r'rock', template[1], re.M | re.I):
                label = self.TILES.MOUNTAIN.value
            elif re.search(r'door', template[1], re.M | re.I):
                label = self.TILES.DOOR.value
            elif re.search(r'gravel', template[1], re.M | re.I):
                label = self.TILES.GRAVEL.value
            elif re.search(r'shopkeeper', template[1], re.M | re.I):
                label = self.TILES.WEAPON_SHOPKEEPER.value
            elif re.search(r'blacksmith', template[1], re.M | re.I):
                label = self.TILES.BLACKSMITH.value
            elif re.search(r'guard', template[1], re.M | re.I):
                label = self.TILES.INACCESSIBLE.value
            elif re.search(r'inaccessible', template[1], re.M | re.I):
                label = self.TILES.INACCESSIBLE.value
            elif re.search(r'accessible', template[1], re.M | re.I):
                label = self.TILES.ACCESSIBLE.value

            # Calculate coordinates of tile in the map relative to the player
            self.game_map[(tile_x, tile_y)] = label

        # Go through all tiles in the gameplay region to find the mountains
        for i, j in zip(*np.where(nearby == self.TILES.MOUNTAIN.value)):
            # Get the tile to the left of the mountain
            tile_left = nearby[(i - 1, j)]

            # Only allow mountains to be minable if they are beside gravel
            if not tile_left == self.TILES.GRAVEL.value:
                nearby[(i, j)] = self.TILES.INACCESSIBLE.value

        # Save the game map to disk
        np.savetxt('map.txt', self.game_map, fmt='%d')