0
0
mirror of https://github.com/ok-oldking/ok-wuthering-waves.git synced 2025-04-24 08:25:16 +00:00

优化寻路刷大地图

This commit is contained in:
firedcto@gmail.com 2025-04-09 21:33:13 +08:00
parent 418a18b7da
commit b6bc9cde5d
53 changed files with 559 additions and 1074 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -501,8 +501,14 @@ msgstr "战斗后自动拾取声骸"
msgid "Farm Map with Star Path" msgid "Farm Map with Star Path"
msgstr "标记寻路刷大地图" msgstr "标记寻路刷大地图"
msgid "Farm world map with a marked path of stars, start in the map screen" msgid "Farm world map with a marked path of stars (diamond as the starting point), start in the map screen"
msgstr "刷用星星标记的路径, 传送到起点后, 在大地图界面开始" msgstr "刷用星星标记的路径, 钻石作为起点, 在大地图界面开始, 工具使用钩锁."
msgid "Stars" msgid "Stars"
msgstr "星星标记" msgstr "星星标记"
msgid "Need be in the map screen and have a diamond as the starting point!"
msgstr "需要在大地图界面并且标记一个钻石为起点!"
msgid "Need be in the map screen and have a path of at least 3 stars!"
msgstr "需要在大地图界面并且至少有3个星星标记为路径!"

View File

@ -56,7 +56,7 @@ numpy==2.2.4
# shapely # shapely
rapidocr==2.0.6 rapidocr==2.0.6
# via -r .\requirements.in # via -r .\requirements.in
ok-script==0.0.524 ok-script==0.0.525
# via -r .\requirements.in # via -r .\requirements.in
omegaconf==2.3.0 omegaconf==2.3.0
# via ok-rapidocr-dml # via ok-rapidocr-dml

View File

@ -28,7 +28,6 @@ class CombatCheck(BaseWWTask):
self.last_in_realm_not_combat = 0 self.last_in_realm_not_combat = 0
self._last_liberation = 0 self._last_liberation = 0
self.target_enemy_time_out = 3 self.target_enemy_time_out = 3
self.check_pick_echo = False
@property @property
def in_liberation(self): def in_liberation(self):
@ -40,6 +39,9 @@ class CombatCheck(BaseWWTask):
if value: if value:
self._last_liberation = time.time() self._last_liberation = time.time()
def on_combat_check(self):
return True
def reset_to_false(self, recheck=False, reason=""): def reset_to_false(self, recheck=False, reason=""):
if self.should_check_monthly_card() and self.handle_monthly_card(): if self.should_check_monthly_card() and self.handle_monthly_card():
return True return True
@ -111,8 +113,9 @@ class CombatCheck(BaseWWTask):
now = time.time() now = time.time()
if now - self.last_combat_check > self.combat_check_interval: if now - self.last_combat_check > self.combat_check_interval:
self.last_combat_check = now self.last_combat_check = now
if self.check_pick_echo: if not self.on_combat_check():
self.incr_drop(self.pick_f()) self.log_info('on_combat_check failed')
return False
if self.has_target(): if self.has_target():
self.last_in_realm_not_combat = 0 self.last_in_realm_not_combat = 0
return True return True

View File

@ -139,12 +139,15 @@ class BaseWWTask(BaseTask):
return None return None
return f return f
def walk_to_box(self, find_function, time_out=30, end_condition=None, y_offset=0.05): def walk_to_box(self, find_function, time_out=30, end_condition=None, y_offset=0.05, v_move_fix_time=0):
if not find_function: if not find_function:
self.log_info('find_function not found, break') self.log_info('find_function not found, break')
return False return False
last_direction = None last_direction = None
v_fix_count = 0
original_y_offset = y_offset
start = time.time() start = time.time()
last_v_move = start
ended = False ended = False
while time.time() - start < time_out: while time.time() - start < time_out:
self.next_frame() self.next_frame()
@ -158,17 +161,27 @@ class BaseWWTask(BaseTask):
treasure_icon = treasure_icon[0] treasure_icon = treasure_icon[0]
else: else:
treasure_icon = None treasure_icon = None
if not treasure_icon: next_direction = None
if treasure_icon is None:
if not end_condition: if not end_condition:
self.log_info('find_function not found, break') self.log_info('find_function not found, break')
break break
if 0 < v_move_fix_time < time.time() - last_v_move:
if v_fix_count > 3:
v_fix_count += 1
y_offset = original_y_offset + 0.05 * v_fix_count
else: else:
self.next_frame() v_fix_count += 1
continue y_offset = original_y_offset - 0.05 * (v_fix_count - 4)
x, y = treasure_icon.center()
if next_direction is None:
x, y = treasure_icon.center()
y = max(0, y - self.height_of_screen(y_offset))
next_direction = self.get_direction(x, y, self.width, self.height)
if next_direction == 'w' or next_direction == 's':
last_v_move = time.time()
y = max(0, y - self.height_of_screen(y_offset))
next_direction = self.get_direction(x, y, self.width, self.height)
if next_direction != last_direction: if next_direction != last_direction:
if last_direction: if last_direction:
self.send_key_up(last_direction) self.send_key_up(last_direction)
@ -184,6 +197,18 @@ class BaseWWTask(BaseTask):
else: else:
return ended return ended
def opposite_direction(self, direction):
if direction == 'w':
return 's'
elif direction == 's':
return 'w'
elif direction == 'a':
return 'd'
elif direction == 'd':
return 'a'
else:
return 'w'
def get_direction(self, location_x, location_y, screen_width, screen_height): def get_direction(self, location_x, location_y, screen_width, screen_height):
""" """
Determines the location (w, a, s, d) based on diagonals Determines the location (w, a, s, d) based on diagonals
@ -413,7 +438,11 @@ class BaseWWTask(BaseTask):
""" """
# Load the ONNX model # Load the ONNX model
boxes = og.my_app.yolo_detect(self.frame, threshold=threshold, label=12) boxes = og.my_app.yolo_detect(self.frame, threshold=threshold, label=12)
ret = sorted(boxes, key=lambda detection: detection.x, reverse=True) ret = sorted(boxes, key=lambda detection: detection.x, reverse=True)
for box in ret:
box.y += box.height - 1
box.height = 1
return ret return ret
def yolo_find_all(self, threshold=0.3): def yolo_find_all(self, threshold=0.3):
@ -464,7 +493,7 @@ class BaseWWTask(BaseTask):
self.log_debug(f'max_echo_count {max_echo_count}') self.log_debug(f'max_echo_count {max_echo_count}')
if echos: if echos:
self.log_info(f'yolo found echo {echos}') self.log_info(f'yolo found echo {echos}')
return self.walk_to_box(self.find_echo, time_out=15, end_condition=self.pick_echo, y_offset=0.2), max_echo_count > 1 return self.walk_to_box(self.find_echo, time_out=15, end_condition=self.pick_echo, v_move_fix_time=5), max_echo_count > 1
if use_color: if use_color:
color_percent = self.calculate_color_percentage(echo_color, front_box) color_percent = self.calculate_color_percentage(echo_color, front_box)
self.log_debug(f'pick_echo color_percent:{color_percent}') self.log_debug(f'pick_echo color_percent:{color_percent}')
@ -660,33 +689,22 @@ class BaseWWTask(BaseTask):
raise Exception("can't find gray_book_boss, make sure f2 is the hotkey for book") raise Exception("can't find gray_book_boss, make sure f2 is the hotkey for book")
return gray_book_boss return gray_book_boss
def click_traval_button(self, use_custom=False): def click_traval_button(self):
if feature := self.find_one(['fast_travel_custom', 'remove_custom', 'gray_teleport'], threshold=0.6): if feature := self.find_first_match_in_box('bottom_right', ['fast_travel_custom', 'gray_teleport', 'remove_custom'], threshold=0.8):
if feature.name == 'gray_teleport': self.click(feature, after_sleep=1)
if use_custom: if feature.name == 'fast_travel_custom':
# if not self.wait_click_feature('custom_teleport_hcenter_vcenter', raise_if_not_found=False, time_out=3):
self.click_relative(0.5, 0.5, after_sleep=1)
# if self.wait_click_feature('gray_custom_way_point', raise_if_not_found=False, time_out=4):
# self.sleep(1)
self.click_relative(0.68, 0.6, after_sleep=1)
self.click_relative(0.74, 0.92, after_sleep=1)
return True
else:
self.click_relative(0.74, 0.92, after_sleep=1)
if self.wait_click_feature(['confirm_btn_hcenter_vcenter', 'confirm_btn_highlight_hcenter_vcenter'], if self.wait_click_feature(['confirm_btn_hcenter_vcenter', 'confirm_btn_highlight_hcenter_vcenter'],
relative_x=-1, raise_if_not_found=True, relative_x=-1, raise_if_not_found=False,
threshold=0.6, threshold=0.6,
time_out=4): time_out=2):
self.wait_click_feature(['confirm_btn_hcenter_vcenter', 'confirm_btn_highlight_hcenter_vcenter'], self.wait_click_feature(['confirm_btn_hcenter_vcenter', 'confirm_btn_highlight_hcenter_vcenter'],
relative_x=-1, raise_if_not_found=False, relative_x=-1, raise_if_not_found=False,
threshold=0.6, threshold=0.6,
time_out=1) time_out=1)
return True return True
elif btn := self.find_one('gray_teleport', threshold=0.7):
return self.click_box(btn, relative_x=1)
def wait_click_travel(self, use_custom=False): def wait_click_travel(self):
self.wait_until(lambda: self.click_traval_button(use_custom=use_custom), raise_if_not_found=True, time_out=10, self.wait_until(self.click_traval_button, raise_if_not_found=True, time_out=10,
settle_time=1) settle_time=1)
def wait_book(self, feature="gray_book_all_monsters"): def wait_book(self, feature="gray_book_all_monsters"):

View File

@ -18,38 +18,43 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.big_map_frame = None self.big_map_frame = None
self.stars = None self.stars = None
self.bounding_box = None
self.sorted = False
self.my_box = None self.my_box = None
self.diamond = None
def reset(self): def reset(self):
self.big_map_frame = None self.big_map_frame = None
self.stars = None self.stars = None
self.bounding_box = None
self.sorted = False
self.my_box = None self.my_box = None
self.diamond = None
def load_stars(self): def load_stars(self, wait_world=True):
self.reset() self.reset()
self.click_relative(0.94, 556 / 1080, after_sleep=1) self.click_relative(0.94, 556 / 1080, after_sleep=1)
self.big_map_frame = self.frame self.big_map_frame = self.frame
self.diamond = self.find_one('big_map_diamond', threshold=0.7, frame=self.big_map_frame, box=Box(0,0,self.big_map_frame.shape[1],self.big_map_frame.shape[0]))
if not self.diamond:
raise Exception('Need be in the map screen and have a diamond as the starting point!')
self.stars = self.find_feature('big_map_star', threshold=0.7, frame=self.big_map_frame, box=Box(0,0,self.big_map_frame.shape[1],self.big_map_frame.shape[0])) self.stars = self.find_feature('big_map_star', threshold=0.7, frame=self.big_map_frame, box=Box(0,0,self.big_map_frame.shape[1],self.big_map_frame.shape[0]))
all_star_len = len(self.stars) all_star_len = len(self.stars)
self.stars = group_boxes_by_center_distance(self.stars, self.height_of_screen(0.2)) self.stars = sort_stars(self.stars, self.diamond, self.height_of_screen(0.2))
self.stars.insert(0, self.diamond)
mini_map_box = self.get_box_by_name('box_minimap')
self.my_box = self.diamond.scale(mini_map_box.width/self.diamond.width * 2)
# if self.debug:
# init_my_box = self.my_box.crop_frame(self.frame)
# self.screenshot('init_my_box', frame=init_my_box)
if len(self.stars) <= 2: if len(self.stars) <= 2:
raise Exception('Need be in the map screen and have a path of at least 3 stars!') raise Exception('Need be in the map screen and have a path of at least 3 stars!')
self.log_info(f'Loaded {len(self.stars)} from {all_star_len} Stars', notify=True)
self.bounding_box = get_bounding_box(self.stars)
mini_map_box = self.get_box_by_name('box_minimap')
self.bounding_box.width += mini_map_box.width * 2
self.bounding_box.height += mini_map_box.height * 2
self.bounding_box.x -= mini_map_box.width
self.bounding_box.y -= mini_map_box.height
self.log_info(f'Loaded {len(self.stars)} from {all_star_len + 1} Stars', notify=True)
# self.click(self.diamond, after_sleep=1)
# self.wait_click_travel()
self.send_key('esc')
self.info_set('Stars', len(self.stars)) self.info_set('Stars', len(self.stars))
self.send_key('esc', after_sleep=1) if wait_world:
self.wait_in_team_and_world()
def get_angle_between(self, my_angle, angle): def get_angle_between(self, my_angle, angle):
if my_angle > angle: if my_angle > angle:
@ -63,9 +68,9 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
return to_turn return to_turn
def get_my_angle(self): def get_my_angle(self):
return self.rotate_arrow_and_find('box_arrow')[0] return self.rotate_arrow_and_find()[0]
def rotate_arrow_and_find(self, box): def rotate_arrow_and_find(self):
arrow_template = self.get_feature_by_name('arrow') arrow_template = self.get_feature_by_name('arrow')
original_mat = arrow_template.mat original_mat = arrow_template.mat
max_conf = 0 max_conf = 0
@ -75,7 +80,7 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
(h, w) = arrow_template.mat.shape[:2] (h, w) = arrow_template.mat.shape[:2]
# self.log_debug(f'turn_east h:{h} w:{w}') # self.log_debug(f'turn_east h:{h} w:{w}')
center = (w // 2, h // 2) center = (w // 2, h // 2)
target_box = self.get_box_by_name(box) if isinstance(box, str) else box target_box = self.get_box_by_name('arrow')
# if self.debug: # if self.debug:
# self.screenshot('arrow_original', original_ mat) # self.screenshot('arrow_original', original_ mat)
for angle in range(0, 360): for angle in range(0, 360):
@ -112,36 +117,11 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
min_star = star min_star = star
return min_star return min_star
def sort_stars(self, my_box):
if self.sorted:
return
remaining_boxes = self.stars[:] # Make a copy
sorted_boxes = []
# Start with the first box in the original list
current_box = self.find_closest(my_box)
remaining_boxes.remove(current_box)
sorted_boxes.append(current_box)
while remaining_boxes:
min_dist = float('inf')
best_idx = -1
# Find the box in remaining_boxes closest to the current_box
for i, box in enumerate(remaining_boxes):
dist = current_box.center_distance(box)
if dist < min_dist:
min_dist = dist
best_idx = i
# Add the closest box to the sorted list and remove from remaining
next_box = remaining_boxes.pop(best_idx)
sorted_boxes.append(next_box)
current_box = next_box # Update the reference point
self.stars = sorted_boxes
self.sorted = True
def find_direction_angle(self, screenshot=False): def find_direction_angle(self, screenshot=False):
if len(self.stars) == 0: if len(self.stars) == 0:
return None, 0, 0 return None, 0, 0
my_box = self.find_my_location(screenshot=screenshot) my_box = self.find_my_location(screenshot=screenshot)
self.sort_stars(my_box) sort_stars(self.stars, my_box,0)
min_star = self.stars[0] min_star = self.stars[0]
min_distance = my_box.center_distance(min_star) min_distance = my_box.center_distance(min_star)
self.draw_boxes('star', min_star, color='green') self.draw_boxes('star', min_star, color='green')
@ -160,37 +140,26 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
def find_my_location(self, screenshot=False): def find_my_location(self, screenshot=False):
frame = self.big_map_frame frame = self.big_map_frame
mat = self.get_box_by_name('box_minimap').crop_frame(self.frame) mat = self.get_box_by_name('box_minimap').crop_frame(self.frame)
mat = keep_circle(mat) # mask = create_circle_mask_with_hole(mat)
# in_big_map = self.find_one(frame=frame, template=mat, threshold=0.01, match_method=cv2.TM_SQDIFF_NORMED, box=self.bounding_box, mask_function=create_circle_mask_with_hole, screenshot=screenshot, canny_lower=50, canny_higher=150) # mat = cv2.bitwise_and(mat, mat, mask=mask)
in_big_map = self.find_one(frame=frame, template=mat, threshold=0.01,
box=self.my_box.scale(1.3) if self.my_box is not None else self.bounding_box, mask_function=create_circle_mask_with_hole, in_big_map = self.find_one(frame=frame, template=mat, threshold=0.2,
box=self.my_box, mask_function=create_circle_mask_with_hole,
screenshot=screenshot) screenshot=screenshot)
# in_big_maps = self.find_feature(frame=frame, template=mat, threshold=0.01, box=self.bounding_box) # in_big_maps = self.find_feature(frame=frame, template=mat, threshold=0.01, box=self.bounding_box)
if not in_big_map: if not in_big_map:
raise RuntimeError('can not find my cords on big map!') raise RuntimeError('can not find my cords on big map!')
self.log_debug(f'found big map: {in_big_map}') self.log_debug(f'found in_big_map: {in_big_map}')
if self.debug and in_big_map: if self.debug and in_big_map:
self.draw_boxes('stars', self.stars) self.draw_boxes('stars', self.stars)
self.draw_boxes('search_map', self.bounding_box) self.draw_boxes('my_box', self.my_box, color='green')
self.draw_boxes('in_big_map', in_big_map, color='yellow') self.draw_boxes('in_big_map', in_big_map, color='yellow')
self.draw_boxes('me', in_big_map.scale(0.1), color='blue') self.draw_boxes('me', in_big_map.scale(0.1), color='blue')
# self.screenshot('box_minimap', frame=frame, show_box=True) # self.screenshot('box_minimap', frame=frame, show_box=True)
self.my_box = in_big_map # self.screenshot('template_minimap', frame=mat)
self.my_box = in_big_map.scale(1.25)
return in_big_map return in_big_map
def keep_circle(img):
height, width = img.shape[:2]
# Create a black mask with the same dimensions
mask = np.zeros((height, width), dtype=np.uint8)
# Define circle parameters (center and radius)
center_x, center_y = width // 2, height // 2
radius = min(center_x, center_y) # Fit circle within image bounds
# Draw a filled white circle on the mask
cv2.circle(mask, (center_x, center_y), radius, (255), thickness=-1)
# Apply the mask to the original image using bitwise AND
result = cv2.bitwise_and(img, img, mask=mask)
return result
def create_circle_mask_with_hole(image): def create_circle_mask_with_hole(image):
""" """
Creates a binary circular mask with a rectangular hole in the center. Creates a binary circular mask with a rectangular hole in the center.
@ -226,17 +195,16 @@ class FarmMapTask(BigMap):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.icon = FluentIcon.GLOBE self.icon = FluentIcon.GLOBE
self.description = "Farm world map with a marked path of stars, start in the map screen" self.description = "Farm world map with a marked path of stars (diamond as the starting point), start in the map screen"
self.name = "Farm Map with Star Path" self.name = "Farm Map with Star Path"
self.max_star_distance = 1000 self.max_star_distance = 1000
self.stuck_keys = [['space', 0.2], ['a',2], ['d',2]] self.stuck_keys = [['space', 0.02], ['a',2], ['d',2], ['t', 0.02]]
self.stuck_index = 0 self.stuck_index = 0
self.last_distance = 0 self.last_distance = 0
self.check_pick_echo = True
@property @property
def star_move_distance_threshold(self): def star_move_distance_threshold(self):
return self.height_of_screen(0.025) return self.height_of_screen(0.03)
def run(self): def run(self):
self.stuck_index = 0 self.stuck_index = 0
@ -244,6 +212,11 @@ class FarmMapTask(BigMap):
self.load_stars() self.load_stars()
self.go_to_star() self.go_to_star()
def on_combat_check(self):
self.incr_drop(self.pick_f())
self.find_my_location()
return True
def go_to_star(self): def go_to_star(self):
current_direction = None current_direction = None
self.center_camera() self.center_camera()
@ -253,17 +226,22 @@ class FarmMapTask(BigMap):
self.sleep(0.01) self.sleep(0.01)
self.middle_click(interval=1, after_sleep=0.2) self.middle_click(interval=1, after_sleep=0.2)
if self.in_combat(): if self.in_combat():
self.sleep(2)
if current_direction is not None: if current_direction is not None:
self.mouse_up(key='right') self.mouse_up(key='right')
self.send_key_up(current_direction) self.send_key_up(current_direction)
current_direction = None current_direction = None
start = time.time()
self.combat_once() self.combat_once()
while True: duration = time.time() - start
dropped, has_more = self.yolo_find_echo(use_color=False, walk=False) if duration > 8:
self.incr_drop(dropped) self.my_box = self.my_box.scale(1.1)
self.sleep(0.5) while True:
if not dropped or not has_more: dropped, has_more = self.yolo_find_echo(use_color=False, walk=False)
break self.incr_drop(dropped)
self.sleep(0.5)
if not dropped or not has_more:
break
star, distance, angle = self.find_direction_angle() star, distance, angle = self.find_direction_angle()
# self.draw_boxes('next_star', star, color='green') # self.draw_boxes('next_star', star, color='green')
if not star: if not star:
@ -284,17 +262,17 @@ class FarmMapTask(BigMap):
else: else:
continue continue
elif distance == self.last_distance: elif distance == self.last_distance:
logger.info(f'might be stuck, try {[self.stuck_index % 3]}') logger.info(f'might be stuck, try {[self.stuck_index % 4]}')
self.send_key(self.stuck_keys[self.stuck_index % 3][0], down_time=self.stuck_keys[self.stuck_index % 3][1], after_sleep=0.5) self.send_key(self.stuck_keys[self.stuck_index % 4][0], down_time=self.stuck_keys[self.stuck_index % 4][1], after_sleep=0.5)
self.stuck_index += 1 self.stuck_index += 1
continue continue
self.last_distance = distance self.last_distance = distance
if current_direction == 'w': if current_direction == 'w':
if 15 <= angle <= 75: if 10 <= angle <= 80:
minor_adjust = 'd' minor_adjust = 'd'
elif -75 <= angle <= -15: elif -80 <= angle <= -10:
minor_adjust = 'a' minor_adjust = 'a'
else: else:
minor_adjust = None minor_adjust = None
@ -305,7 +283,7 @@ class FarmMapTask(BigMap):
self.sleep(0.1) self.sleep(0.1)
self.middle_click(down_time=0.1) self.middle_click(down_time=0.1)
self.send_key_up(minor_adjust) self.send_key_up(minor_adjust)
self.sleep(0.2) self.sleep(0.01)
continue continue
if current_adjust: if current_adjust:
self.send_key_up(current_adjust) self.send_key_up(current_adjust)
@ -320,9 +298,8 @@ class FarmMapTask(BigMap):
new_direction = 's' new_direction = 's'
if current_direction != new_direction: if current_direction != new_direction:
self.log_info(f'changed direction {angle} {current_direction} -> {new_direction}') self.log_info(f'changed direction {angle} {current_direction} -> {new_direction}')
if self.debug:
self.screenshot(f'{current_direction}_{new_direction}_{angle}')
if current_direction: if current_direction:
self.mouse_up(key='right')
self.send_key_up(current_direction) self.send_key_up(current_direction)
self.sleep(0.2) self.sleep(0.2)
self.turn_direction(new_direction) self.turn_direction(new_direction)
@ -359,38 +336,36 @@ star_color = {
'b': (190, 220) # Blue range 'b': (190, 220) # Blue range
} }
def group_boxes_by_center_distance(boxes: List[Box], distance_threshold: float) -> List[Box]:
def sort_stars(points, start_point, max_distance = 0):
""" """
Groups boxes where any box is close (center_distance < threshold) BUILDS A PATH using Nearest Neighbor heuristic starting from 'start_point'.
to any other box in the group (connected components). Filters steps where distance < 'min_distance'. Prepends start_point.
Returns the largest group. NOTE: This is a HEURISTIC, likely NOT the absolute shortest total path.
""" """
if not boxes: unvisited = points[:] # Copy the list of points to visit
if not unvisited: # Handle empty input list
return [] return []
n = len(boxes)
visited = [False] * n path_result = [] # Initialize empty list for the results (excluding start)
all_groups = [] current_point = start_point # Start the calculation from start_point
# Find connected components using DFS while unvisited:
for i in range(n): # Find points reachable according to min_distance
if not visited[i]: # If min_distance is 0, distance(..) >= 0 is always true for distinct points.
current_group = [] reachable_points = [p for p in unvisited if max_distance == 0 or current_point.center_distance(p) <= max_distance]
stack = [i] if not reachable_points:
visited[i] = True # Stop if no remaining points meet the criteria (or if unvisited is empty)
while stack: # print(f"Stopping: No remaining points meet criteria from {current_point}.") # Optional debug
current_idx = stack.pop() break
current_group.append(boxes[current_idx]) # Find the closest point among the reachable ones
for j in range(n): next_point = min(reachable_points, key=lambda p: current_point.center_distance(p))
if not visited[j]:
# Use center_distance as requested path_result.append(next_point) # Add the chosen point to the result list
dist = boxes[current_idx].center_distance(boxes[j]) unvisited.remove(next_point) # Mark as visited
if dist < distance_threshold: current_point = next_point # Update the current point for the next iteration
visited[j] = True return path_result
stack.append(j)
all_groups.append(current_group)
# Return the largest group found
if not all_groups:
return [] # Should not happen if boxes is not empty, but safe check
return max(all_groups, key=len)
def mask_star(image): def mask_star(image):
# return image # return image

View File

@ -13,20 +13,20 @@ class TestTacet(TaskTestCase):
def test_find_treasure_icon(self): def test_find_treasure_icon(self):
self.set_image('tests/images/angle_130.png') self.set_image('tests/images/angle_130.png')
angle, box = self.task.get_angle() angle, box = self.task.get_my_angle()
self.logger.info(f'test_find_treasure_icon {angle, box}') self.logger.info(f'test_find_treasure_icon {angle, box}')
self.assertTrue(100 <= angle <= 200) self.assertTrue(100 <= angle <= 200)
def test_find_path(self): def test_find_path(self):
self.set_image('tests/images/path.png') self.set_image('tests/images/path.png')
self.task.load_stars() self.task.load_stars(wait_world=False)
self.set_image('tests/images/mini_map.png') self.set_image('tests/images/mini_map.png')
self.task.my_box = self.task.box_of_screen(0.45, 0.17, 0.62, 0.54) # self.task.my_box = self.task.box_of_screen(0.45, 0.17, 0.62, 0.54)
star, distance, angle = self.task.find_direction_angle(screenshot=True) star, distance, angle = self.task.find_direction_angle(screenshot=True)
time.sleep(5) time.sleep(5)
self.logger.info(f'test_find_path {star, distance, angle}') self.logger.info(f'test_find_path {star, distance, angle}')
self.assertTrue(1 <= distance <= 20) self.assertTrue(1 <= distance <= 50)
if __name__ == '__main__': if __name__ == '__main__':

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB