优化寻路刷大地图
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 164 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 202 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 164 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 202 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 39 KiB |
1325
assets/result.json
@ -501,8 +501,14 @@ msgstr "战斗后自动拾取声骸"
|
||||
msgid "Farm Map with Star Path"
|
||||
msgstr "标记寻路刷大地图"
|
||||
|
||||
msgid "Farm world map with a marked path of stars, start in the map screen"
|
||||
msgstr "刷用星星标记的路径, 传送到起点后, 在大地图界面开始"
|
||||
msgid "Farm world map with a marked path of stars (diamond as the starting point), start in the map screen"
|
||||
msgstr "刷用星星标记的路径, 钻石作为起点, 在大地图界面开始, 工具使用钩锁."
|
||||
|
||||
msgid "Stars"
|
||||
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个星星标记为路径!"
|
@ -56,7 +56,7 @@ numpy==2.2.4
|
||||
# shapely
|
||||
rapidocr==2.0.6
|
||||
# via -r .\requirements.in
|
||||
ok-script==0.0.524
|
||||
ok-script==0.0.525
|
||||
# via -r .\requirements.in
|
||||
omegaconf==2.3.0
|
||||
# via ok-rapidocr-dml
|
||||
|
@ -28,7 +28,6 @@ class CombatCheck(BaseWWTask):
|
||||
self.last_in_realm_not_combat = 0
|
||||
self._last_liberation = 0
|
||||
self.target_enemy_time_out = 3
|
||||
self.check_pick_echo = False
|
||||
|
||||
@property
|
||||
def in_liberation(self):
|
||||
@ -40,6 +39,9 @@ class CombatCheck(BaseWWTask):
|
||||
if value:
|
||||
self._last_liberation = time.time()
|
||||
|
||||
def on_combat_check(self):
|
||||
return True
|
||||
|
||||
def reset_to_false(self, recheck=False, reason=""):
|
||||
if self.should_check_monthly_card() and self.handle_monthly_card():
|
||||
return True
|
||||
@ -111,8 +113,9 @@ class CombatCheck(BaseWWTask):
|
||||
now = time.time()
|
||||
if now - self.last_combat_check > self.combat_check_interval:
|
||||
self.last_combat_check = now
|
||||
if self.check_pick_echo:
|
||||
self.incr_drop(self.pick_f())
|
||||
if not self.on_combat_check():
|
||||
self.log_info('on_combat_check failed')
|
||||
return False
|
||||
if self.has_target():
|
||||
self.last_in_realm_not_combat = 0
|
||||
return True
|
||||
|
@ -139,12 +139,15 @@ class BaseWWTask(BaseTask):
|
||||
return None
|
||||
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:
|
||||
self.log_info('find_function not found, break')
|
||||
return False
|
||||
last_direction = None
|
||||
v_fix_count = 0
|
||||
original_y_offset = y_offset
|
||||
start = time.time()
|
||||
last_v_move = start
|
||||
ended = False
|
||||
while time.time() - start < time_out:
|
||||
self.next_frame()
|
||||
@ -158,17 +161,27 @@ class BaseWWTask(BaseTask):
|
||||
treasure_icon = treasure_icon[0]
|
||||
else:
|
||||
treasure_icon = None
|
||||
if not treasure_icon:
|
||||
next_direction = None
|
||||
if treasure_icon is None:
|
||||
if not end_condition:
|
||||
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:
|
||||
self.next_frame()
|
||||
continue
|
||||
x, y = treasure_icon.center()
|
||||
v_fix_count += 1
|
||||
y_offset = original_y_offset - 0.05 * (v_fix_count - 4)
|
||||
|
||||
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 last_direction:
|
||||
self.send_key_up(last_direction)
|
||||
@ -184,6 +197,18 @@ class BaseWWTask(BaseTask):
|
||||
else:
|
||||
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):
|
||||
"""
|
||||
Determines the location (w, a, s, d) based on diagonals
|
||||
@ -413,7 +438,11 @@ class BaseWWTask(BaseTask):
|
||||
"""
|
||||
# Load the ONNX model
|
||||
boxes = og.my_app.yolo_detect(self.frame, threshold=threshold, label=12)
|
||||
|
||||
ret = sorted(boxes, key=lambda detection: detection.x, reverse=True)
|
||||
for box in ret:
|
||||
box.y += box.height - 1
|
||||
box.height = 1
|
||||
return ret
|
||||
|
||||
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}')
|
||||
if 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:
|
||||
color_percent = self.calculate_color_percentage(echo_color, front_box)
|
||||
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")
|
||||
return gray_book_boss
|
||||
|
||||
def click_traval_button(self, use_custom=False):
|
||||
if feature := self.find_one(['fast_travel_custom', 'remove_custom', 'gray_teleport'], threshold=0.6):
|
||||
if feature.name == 'gray_teleport':
|
||||
if use_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)
|
||||
def click_traval_button(self):
|
||||
if feature := self.find_first_match_in_box('bottom_right', ['fast_travel_custom', 'gray_teleport', 'remove_custom'], threshold=0.8):
|
||||
self.click(feature, after_sleep=1)
|
||||
if feature.name == 'fast_travel_custom':
|
||||
if self.wait_click_feature(['confirm_btn_hcenter_vcenter', 'confirm_btn_highlight_hcenter_vcenter'],
|
||||
relative_x=-1, raise_if_not_found=True,
|
||||
threshold=0.6,
|
||||
time_out=4):
|
||||
relative_x=-1, raise_if_not_found=False,
|
||||
threshold=0.6,
|
||||
time_out=2):
|
||||
self.wait_click_feature(['confirm_btn_hcenter_vcenter', 'confirm_btn_highlight_hcenter_vcenter'],
|
||||
relative_x=-1, raise_if_not_found=False,
|
||||
threshold=0.6,
|
||||
time_out=1)
|
||||
return True
|
||||
elif btn := self.find_one('gray_teleport', threshold=0.7):
|
||||
return self.click_box(btn, relative_x=1)
|
||||
relative_x=-1, raise_if_not_found=False,
|
||||
threshold=0.6,
|
||||
time_out=1)
|
||||
return True
|
||||
|
||||
def wait_click_travel(self, use_custom=False):
|
||||
self.wait_until(lambda: self.click_traval_button(use_custom=use_custom), raise_if_not_found=True, time_out=10,
|
||||
def wait_click_travel(self):
|
||||
self.wait_until(self.click_traval_button, raise_if_not_found=True, time_out=10,
|
||||
settle_time=1)
|
||||
|
||||
def wait_book(self, feature="gray_book_all_monsters"):
|
||||
|
@ -18,38 +18,43 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.big_map_frame = None
|
||||
self.stars = None
|
||||
self.bounding_box = None
|
||||
self.sorted = False
|
||||
self.my_box = None
|
||||
self.diamond = None
|
||||
|
||||
|
||||
def reset(self):
|
||||
self.big_map_frame = None
|
||||
self.stars = None
|
||||
self.bounding_box = None
|
||||
self.sorted = False
|
||||
self.my_box = None
|
||||
self.diamond = None
|
||||
|
||||
def load_stars(self):
|
||||
def load_stars(self, wait_world=True):
|
||||
self.reset()
|
||||
self.click_relative(0.94, 556 / 1080, after_sleep=1)
|
||||
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]))
|
||||
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:
|
||||
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.send_key('esc', after_sleep=1)
|
||||
|
||||
if wait_world:
|
||||
self.wait_in_team_and_world()
|
||||
|
||||
def get_angle_between(self, my_angle, angle):
|
||||
if my_angle > angle:
|
||||
@ -63,9 +68,9 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
|
||||
return to_turn
|
||||
|
||||
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')
|
||||
original_mat = arrow_template.mat
|
||||
max_conf = 0
|
||||
@ -75,7 +80,7 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
|
||||
(h, w) = arrow_template.mat.shape[:2]
|
||||
# self.log_debug(f'turn_east h:{h} w:{w}')
|
||||
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:
|
||||
# self.screenshot('arrow_original', original_ mat)
|
||||
for angle in range(0, 360):
|
||||
@ -112,36 +117,11 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
|
||||
min_star = 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):
|
||||
if len(self.stars) == 0:
|
||||
return None, 0, 0
|
||||
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_distance = my_box.center_distance(min_star)
|
||||
self.draw_boxes('star', min_star, color='green')
|
||||
@ -160,37 +140,26 @@ class BigMap(WWOneTimeTask, BaseCombatTask):
|
||||
def find_my_location(self, screenshot=False):
|
||||
frame = self.big_map_frame
|
||||
mat = self.get_box_by_name('box_minimap').crop_frame(self.frame)
|
||||
mat = keep_circle(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)
|
||||
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,
|
||||
# mask = create_circle_mask_with_hole(mat)
|
||||
# mat = cv2.bitwise_and(mat, mat, mask=mask)
|
||||
|
||||
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)
|
||||
# in_big_maps = self.find_feature(frame=frame, template=mat, threshold=0.01, box=self.bounding_box)
|
||||
if not in_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:
|
||||
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('me', in_big_map.scale(0.1), color='blue')
|
||||
# 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
|
||||
|
||||
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):
|
||||
"""
|
||||
Creates a binary circular mask with a rectangular hole in the center.
|
||||
@ -226,17 +195,16 @@ class FarmMapTask(BigMap):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
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.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.last_distance = 0
|
||||
self.check_pick_echo = True
|
||||
|
||||
@property
|
||||
def star_move_distance_threshold(self):
|
||||
return self.height_of_screen(0.025)
|
||||
return self.height_of_screen(0.03)
|
||||
|
||||
def run(self):
|
||||
self.stuck_index = 0
|
||||
@ -244,6 +212,11 @@ class FarmMapTask(BigMap):
|
||||
self.load_stars()
|
||||
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):
|
||||
current_direction = None
|
||||
self.center_camera()
|
||||
@ -253,17 +226,22 @@ class FarmMapTask(BigMap):
|
||||
self.sleep(0.01)
|
||||
self.middle_click(interval=1, after_sleep=0.2)
|
||||
if self.in_combat():
|
||||
self.sleep(2)
|
||||
if current_direction is not None:
|
||||
self.mouse_up(key='right')
|
||||
self.send_key_up(current_direction)
|
||||
current_direction = None
|
||||
start = time.time()
|
||||
self.combat_once()
|
||||
while True:
|
||||
dropped, has_more = self.yolo_find_echo(use_color=False, walk=False)
|
||||
self.incr_drop(dropped)
|
||||
self.sleep(0.5)
|
||||
if not dropped or not has_more:
|
||||
break
|
||||
duration = time.time() - start
|
||||
if duration > 8:
|
||||
self.my_box = self.my_box.scale(1.1)
|
||||
while True:
|
||||
dropped, has_more = self.yolo_find_echo(use_color=False, walk=False)
|
||||
self.incr_drop(dropped)
|
||||
self.sleep(0.5)
|
||||
if not dropped or not has_more:
|
||||
break
|
||||
star, distance, angle = self.find_direction_angle()
|
||||
# self.draw_boxes('next_star', star, color='green')
|
||||
if not star:
|
||||
@ -284,17 +262,17 @@ class FarmMapTask(BigMap):
|
||||
else:
|
||||
continue
|
||||
elif distance == self.last_distance:
|
||||
logger.info(f'might be stuck, try {[self.stuck_index % 3]}')
|
||||
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)
|
||||
logger.info(f'might be stuck, try {[self.stuck_index % 4]}')
|
||||
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
|
||||
continue
|
||||
|
||||
self.last_distance = distance
|
||||
|
||||
if current_direction == 'w':
|
||||
if 15 <= angle <= 75:
|
||||
if 10 <= angle <= 80:
|
||||
minor_adjust = 'd'
|
||||
elif -75 <= angle <= -15:
|
||||
elif -80 <= angle <= -10:
|
||||
minor_adjust = 'a'
|
||||
else:
|
||||
minor_adjust = None
|
||||
@ -305,7 +283,7 @@ class FarmMapTask(BigMap):
|
||||
self.sleep(0.1)
|
||||
self.middle_click(down_time=0.1)
|
||||
self.send_key_up(minor_adjust)
|
||||
self.sleep(0.2)
|
||||
self.sleep(0.01)
|
||||
continue
|
||||
if current_adjust:
|
||||
self.send_key_up(current_adjust)
|
||||
@ -320,9 +298,8 @@ class FarmMapTask(BigMap):
|
||||
new_direction = 's'
|
||||
if 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:
|
||||
self.mouse_up(key='right')
|
||||
self.send_key_up(current_direction)
|
||||
self.sleep(0.2)
|
||||
self.turn_direction(new_direction)
|
||||
@ -359,38 +336,36 @@ star_color = {
|
||||
'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)
|
||||
to any other box in the group (connected components).
|
||||
Returns the largest group.
|
||||
BUILDS A PATH using Nearest Neighbor heuristic starting from 'start_point'.
|
||||
Filters steps where distance < 'min_distance'. Prepends start_point.
|
||||
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 []
|
||||
n = len(boxes)
|
||||
visited = [False] * n
|
||||
all_groups = []
|
||||
# Find connected components using DFS
|
||||
for i in range(n):
|
||||
if not visited[i]:
|
||||
current_group = []
|
||||
stack = [i]
|
||||
visited[i] = True
|
||||
while stack:
|
||||
current_idx = stack.pop()
|
||||
current_group.append(boxes[current_idx])
|
||||
for j in range(n):
|
||||
if not visited[j]:
|
||||
# Use center_distance as requested
|
||||
dist = boxes[current_idx].center_distance(boxes[j])
|
||||
if dist < distance_threshold:
|
||||
visited[j] = True
|
||||
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)
|
||||
|
||||
path_result = [] # Initialize empty list for the results (excluding start)
|
||||
current_point = start_point # Start the calculation from start_point
|
||||
while unvisited:
|
||||
# Find points reachable according to min_distance
|
||||
# If min_distance is 0, distance(..) >= 0 is always true for distinct points.
|
||||
reachable_points = [p for p in unvisited if max_distance == 0 or current_point.center_distance(p) <= max_distance]
|
||||
if not reachable_points:
|
||||
# Stop if no remaining points meet the criteria (or if unvisited is empty)
|
||||
# print(f"Stopping: No remaining points meet criteria from {current_point}.") # Optional debug
|
||||
break
|
||||
# Find the closest point among the reachable ones
|
||||
next_point = min(reachable_points, key=lambda p: current_point.center_distance(p))
|
||||
|
||||
path_result.append(next_point) # Add the chosen point to the result list
|
||||
unvisited.remove(next_point) # Mark as visited
|
||||
current_point = next_point # Update the current point for the next iteration
|
||||
return path_result
|
||||
|
||||
|
||||
|
||||
def mask_star(image):
|
||||
# return image
|
||||
|
@ -13,20 +13,20 @@ class TestTacet(TaskTestCase):
|
||||
|
||||
def test_find_treasure_icon(self):
|
||||
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.assertTrue(100 <= angle <= 200)
|
||||
|
||||
def test_find_path(self):
|
||||
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.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)
|
||||
time.sleep(5)
|
||||
self.logger.info(f'test_find_path {star, distance, angle}')
|
||||
self.assertTrue(1 <= distance <= 20)
|
||||
self.assertTrue(1 <= distance <= 50)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 3.0 MiB |
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 1.7 MiB |