mirror of
https://github.com/LmeSzinc/StarRailCopilot.git
synced 2025-02-07 19:29:30 +00:00
Add: 活动AB图每日三倍基本稳定
- 调整了微层混合透视识别的参数 - 增加了在hsv色彩空间下的识别方法 - 增加了对地面闪烁红框的识别 - 增加了已打精英的计数 - 修改精英识别, boss识别, 舰队识别方法为hsv颜色计数 - 修复了自动生成camera_data的报错
This commit is contained in:
parent
ce4887a4f5
commit
8366d7a258
@ -37,7 +37,7 @@ class Config:
|
||||
CAMPAIGN_MODE = 'normal'
|
||||
|
||||
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
|
||||
'height': (50, 200),
|
||||
'height': (50, 255 - 80),
|
||||
'width': 1,
|
||||
'prominence': 10,
|
||||
'distance': 35,
|
||||
@ -46,6 +46,7 @@ class Config:
|
||||
'height': (255 - 80, 255),
|
||||
'prominence': 10,
|
||||
'distance': 50,
|
||||
'width': (0, 7),
|
||||
'wlen': 1000
|
||||
}
|
||||
|
||||
@ -64,3 +65,6 @@ class Campaign(CampaignBase):
|
||||
return True
|
||||
|
||||
return self.battle_default()
|
||||
|
||||
def battle_3(self):
|
||||
return self.clear_boss()
|
||||
|
@ -50,3 +50,6 @@ class Campaign(CampaignBase):
|
||||
return True
|
||||
|
||||
return self.battle_default()
|
||||
|
||||
def battle_4(self):
|
||||
return self.clear_boss()
|
||||
|
@ -57,3 +57,6 @@ class Campaign(CampaignBase):
|
||||
return True
|
||||
|
||||
return self.battle_default()
|
||||
|
||||
def battle_4(self):
|
||||
return self.clear_boss()
|
@ -5,7 +5,7 @@ from module.logger import logger
|
||||
from campaign.event_20200326_cn.a1 import Config
|
||||
|
||||
MAP = CampaignMap()
|
||||
MAP.shape = 'G7'
|
||||
MAP.shape = 'H7'
|
||||
MAP.map_data = '''
|
||||
-- ME -- ME ++ ++ SP SP
|
||||
ME -- MS -- ME -- -- SP
|
||||
@ -48,3 +48,6 @@ class Campaign(CampaignBase):
|
||||
return True
|
||||
|
||||
return self.battle_default()
|
||||
|
||||
def battle_4(self):
|
||||
return self.clear_boss()
|
||||
|
@ -20,6 +20,7 @@ class CampaignRun(CampaignUI):
|
||||
module = None
|
||||
config: AzurLaneConfig
|
||||
campaign: CampaignBase
|
||||
run_count: int
|
||||
|
||||
def load_campaign(self, name, folder='campaign_main'):
|
||||
"""
|
||||
@ -67,57 +68,72 @@ class CampaignRun(CampaignUI):
|
||||
os.mkdir(folder)
|
||||
self.campaign.config.SCREEN_SHOT_SAVE_FOLDER = folder
|
||||
|
||||
def oil_check(self):
|
||||
def triggered_stop_condition(self):
|
||||
"""
|
||||
Returns:
|
||||
bool: If have enough oil.
|
||||
"""
|
||||
if not self.config.STOP_IF_OIL_LOWER_THAN:
|
||||
return True
|
||||
self.device.screenshot()
|
||||
return OCR_OIL.ocr(self.device.image) > self.config.STOP_IF_OIL_LOWER_THAN
|
||||
|
||||
def run(self, name, folder='campaign_main'):
|
||||
Returns:
|
||||
bool: If triggered a stop condition.
|
||||
"""
|
||||
# Run count limit
|
||||
if self.run_count >= self.config.STOP_IF_COUNT_GREATER_THAN > 0:
|
||||
logger.hr('Triggered count stop')
|
||||
return True
|
||||
# Run time limit
|
||||
if self.config.STOP_IF_TIME_REACH and datetime.now() > self.config.STOP_IF_TIME_REACH:
|
||||
logger.hr('Triggered time limit')
|
||||
self.config.config.set('Setting', 'if_time_reach', '0')
|
||||
self.config.save()
|
||||
return True
|
||||
# Oil limit
|
||||
if self.config.STOP_IF_TRIGGER_EMOTION_LIMIT and self.campaign.config.EMOTION_LIMIT_TRIGGERED:
|
||||
logger.hr('Triggered emotion limit')
|
||||
return True
|
||||
# Emotion limit
|
||||
if self.config.STOP_IF_OIL_LOWER_THAN:
|
||||
if OCR_OIL.ocr(self.device.image) < self.config.STOP_IF_OIL_LOWER_THAN:
|
||||
logger.hr('Triggered oil limit')
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def run(self, name, folder='campaign_main', total=0):
|
||||
"""
|
||||
Args:
|
||||
name (str): Name of .py file.
|
||||
folder (str): Name of the file folder under campaign.
|
||||
total (int):
|
||||
"""
|
||||
self.load_campaign(name, folder=folder)
|
||||
n = 0
|
||||
self.run_count = 0
|
||||
while 1:
|
||||
self.device.screenshot()
|
||||
|
||||
# End
|
||||
if n >= self.config.STOP_IF_COUNT_GREATER_THAN > 0:
|
||||
logger.hr('Triggered count stop')
|
||||
if total and self.run_count == total:
|
||||
break
|
||||
if not self.oil_check():
|
||||
logger.hr('Triggered oil limit')
|
||||
break
|
||||
if self.config.STOP_IF_TIME_REACH and datetime.now() > self.config.STOP_IF_TIME_REACH:
|
||||
logger.hr('Triggered time limit')
|
||||
self.config.config.set('Setting', 'if_time_reach', '0')
|
||||
self.config.save()
|
||||
break
|
||||
if self.config.STOP_IF_TRIGGER_EMOTION_LIMIT and self.campaign.config.EMOTION_LIMIT_TRIGGERED:
|
||||
logger.hr('Triggered emotion limit')
|
||||
if self.triggered_stop_condition():
|
||||
break
|
||||
|
||||
# Log
|
||||
logger.hr(name, level=1)
|
||||
if self.config.STOP_IF_COUNT_GREATER_THAN > 0:
|
||||
logger.info(f'Count: [{n}/{self.config.STOP_IF_COUNT_GREATER_THAN}]')
|
||||
logger.info(f'Count: [{self.run_count}/{self.config.STOP_IF_COUNT_GREATER_THAN}]')
|
||||
else:
|
||||
logger.info(f'Count: [{n}]')
|
||||
logger.info(f'Count: [{self.run_count}]')
|
||||
|
||||
# Run
|
||||
self.ensure_campaign_ui(name=self.stage)
|
||||
self.campaign.ENTRANCE = self.campaign_get_entrance(name=self.stage)
|
||||
self.campaign.device.image = self.device.image
|
||||
if self.campaign.is_in_map():
|
||||
logger.info('Already in map, skip ensure_campaign_ui.')
|
||||
else:
|
||||
self.ensure_campaign_ui(name=self.stage)
|
||||
self.campaign.ENTRANCE = self.campaign_get_entrance(name=self.stage)
|
||||
self.campaign.run()
|
||||
|
||||
# After run
|
||||
n += 1
|
||||
self.run_count += 1
|
||||
if self.config.STOP_IF_COUNT_GREATER_THAN > 0:
|
||||
count = self.config.STOP_IF_COUNT_GREATER_THAN - n
|
||||
count = self.config.STOP_IF_COUNT_GREATER_THAN - self.run_count
|
||||
count = 0 if count < 0 else count
|
||||
self.config.config.set('Setting', 'if_count_greater_than', str(count))
|
||||
self.config.save()
|
||||
|
@ -196,6 +196,7 @@ class AzurLaneConfig:
|
||||
'height': (255 - 24, 255),
|
||||
'prominence': 10,
|
||||
'distance': 50,
|
||||
'width': (0, 7),
|
||||
'wlen': 1000
|
||||
}
|
||||
# Parameters for cv2.HoughLines
|
||||
|
@ -6,11 +6,11 @@ CAMPAIGN_NAME = ['a1', 'a2', 'a3', 'b1', 'b2', 'b3']
|
||||
|
||||
|
||||
class CampaignAB(CampaignRun):
|
||||
def run(self, name, folder='campaign_main'):
|
||||
def run(self, name, folder='campaign_main', total=0):
|
||||
name = name.lower()
|
||||
option = ('EventABRecord', name)
|
||||
if not self.config.record_executed_since(option=option, since=RECORD_SINCE):
|
||||
super().run(name=name, folder=folder)
|
||||
super().run(name=name, folder=folder, total=1)
|
||||
self.config.record_save(option=option)
|
||||
|
||||
def run_event_daily(self):
|
||||
|
@ -197,7 +197,7 @@ class Camera(InfoBarHandler):
|
||||
if np.all(np.abs(vector) <= 0):
|
||||
break
|
||||
|
||||
def full_scan(self, battle_count=None, mystery_count=0):
|
||||
def full_scan(self, battle_count=None, mystery_count=0, siren_count=0):
|
||||
"""Scan the hole map.
|
||||
|
||||
Args:
|
||||
@ -217,7 +217,7 @@ class Camera(InfoBarHandler):
|
||||
queue = queue[1:]
|
||||
|
||||
if battle_count is not None:
|
||||
self.map.missing_predict(battle_count=battle_count, mystery_count=mystery_count)
|
||||
self.map.missing_predict(battle_count=battle_count, mystery_count=mystery_count, siren_count=siren_count)
|
||||
self.map.show()
|
||||
|
||||
def in_sight(self, location, sight=(-3, -1, 3, 2)):
|
||||
|
@ -13,6 +13,7 @@ class Fleet(Camera, AmbushHandler, MysteryHandler, MapOperation):
|
||||
fleet_current_index = 1
|
||||
battle_count = 0
|
||||
mystery_count = 0
|
||||
siren_count = 0
|
||||
fleet_ammo = 5
|
||||
ammo_count = 3
|
||||
|
||||
@ -72,6 +73,7 @@ class Fleet(Camera, AmbushHandler, MysteryHandler, MapOperation):
|
||||
arrived = False
|
||||
# Wait to confirm fleet arrived. It does't appear immediately if fleet in combat .
|
||||
arrive_timer = Timer(0.3)
|
||||
arrive_unexpected_timer = Timer(1.5)
|
||||
# Wait after ambushed.
|
||||
ambushed_retry = Timer(0.5)
|
||||
# If nothing happens, click again.
|
||||
@ -102,6 +104,8 @@ class Fleet(Camera, AmbushHandler, MysteryHandler, MapOperation):
|
||||
arrived = True
|
||||
result = 'combat'
|
||||
self.battle_count += 1
|
||||
if 'siren' in expected:
|
||||
self.siren_count += 1
|
||||
self.fleet_ammo -= 1
|
||||
self.map[location_ensure(location)].is_cleared = True
|
||||
self.handle_boss_appear_refocus()
|
||||
@ -113,10 +117,14 @@ class Fleet(Camera, AmbushHandler, MysteryHandler, MapOperation):
|
||||
|
||||
if grid.predict_fleet():
|
||||
arrive_timer.start()
|
||||
arrive_unexpected_timer.start()
|
||||
if not arrive_timer.reached():
|
||||
continue
|
||||
if expected and result != expected:
|
||||
continue
|
||||
if expected and result not in expected:
|
||||
if arrive_unexpected_timer.reached():
|
||||
logger.warning('Arrive with unexpected result')
|
||||
else:
|
||||
continue
|
||||
logger.info('Arrive confirm')
|
||||
arrived = True
|
||||
break
|
||||
@ -202,11 +210,12 @@ class Fleet(Camera, AmbushHandler, MysteryHandler, MapOperation):
|
||||
logger.hr('Map init')
|
||||
self.battle_count = 0
|
||||
self.mystery_count = 0
|
||||
self.siren_count = 0
|
||||
self.ammo_count = 3
|
||||
self.map = map_
|
||||
self.map.reset()
|
||||
self.ensure_edge_insight(preset=self.map.in_map_swipe_preset_data)
|
||||
self.full_scan(battle_count=self.battle_count, mystery_count=self.mystery_count)
|
||||
self.full_scan(battle_count=self.battle_count, mystery_count=self.mystery_count, siren_count=self.siren_count)
|
||||
self.find_current_fleet()
|
||||
self.find_path_initial()
|
||||
|
||||
|
@ -69,12 +69,9 @@ class GridPredictor:
|
||||
self.enemy_scale = self.predict_enemy_scale()
|
||||
if self.enemy_scale > 0:
|
||||
self.is_enemy = True
|
||||
if self.may_siren:
|
||||
if not self.is_enemy:
|
||||
self.is_enemy = self.predict_dynamic_red_border()
|
||||
if self.is_enemy:
|
||||
self.is_siren = True
|
||||
self.is_mystery = self.predict_mystery()
|
||||
if not self.is_enemy and not self.is_mystery:
|
||||
self.is_siren = self.predict_dynamic_red_border()
|
||||
self.is_fleet = self.predict_fleet()
|
||||
self.is_boss = self.predict_boss()
|
||||
# self.image_perspective = color_similarity_2d(
|
||||
@ -152,6 +149,7 @@ class GridPredictor:
|
||||
mask = np.pad(mask, ((pad, pad), (pad, pad)), mode='constant', constant_values=1)
|
||||
image = image * mask
|
||||
image[r < 221] = 0
|
||||
# print(self, np.mean(image))
|
||||
return np.mean(image) > 2
|
||||
|
||||
|
||||
@ -168,6 +166,23 @@ class GridPredictor:
|
||||
count = np.sum(image > color_threshold)
|
||||
return count
|
||||
|
||||
def _relative_image_color_hue_count(self, area, h, s=None, v=None, output_shape=(50, 50)):
|
||||
image = self.get_relative_image(area, output_shape=output_shape)
|
||||
# if str(self) == 'A4':
|
||||
# image.show()
|
||||
hsv = rgb2hsv(np.array(image) / 255)
|
||||
hue = hsv[:, :, 0]
|
||||
count = (h[0] / 360 < hue) & (hue < h[1] / 360)
|
||||
if s:
|
||||
saturation = hsv[:, :, 1]
|
||||
count &= (s[0] / 100 < saturation) & (saturation < s[1] / 100)
|
||||
if v:
|
||||
value = hsv[:, :, 2]
|
||||
count &= (v[0] / 100 < value) & (value < v[1] / 100)
|
||||
|
||||
count = np.sum(count)
|
||||
return count
|
||||
|
||||
def predict_mystery(self):
|
||||
# if not self.may_mystery:
|
||||
# return False
|
||||
@ -184,8 +199,11 @@ class GridPredictor:
|
||||
|
||||
def predict_fleet(self):
|
||||
# white ammo icon
|
||||
return self._relative_image_color_count(
|
||||
area=(-1, -2, -0.5, -1.5), color=(255, 255, 255), color_threshold=252) > 300
|
||||
# return self._relative_image_color_count(
|
||||
# area=(-1, -2, -0.5, -1.5), color=(255, 255, 255), color_threshold=252) > 300
|
||||
count = self._relative_image_color_hue_count(area=(-1, -2, -0.5, -1.5), h=(-3, 3), v=(50, 101))
|
||||
count += self._relative_image_color_hue_count(area=(-1, -2, -0.5, -1.5), h=(180 - 3, 180 + 3), v=(50, 101))
|
||||
return count > 300
|
||||
|
||||
def predict_current_fleet(self):
|
||||
# Green arrow over head with hue around 141.
|
||||
@ -195,6 +213,9 @@ class GridPredictor:
|
||||
return count > 1000
|
||||
|
||||
def predict_boss(self):
|
||||
count = self._relative_image_color_count(
|
||||
area=(-0.5, -0.2, 0.5, 0.2), color=(255, 77, 82), color_threshold=247)
|
||||
return count > 100
|
||||
# count = self._relative_image_color_count(
|
||||
# area=(-0.5, -0.2, 0.5, 0.2), color=(255, 77, 82), color_threshold=247)
|
||||
# return count > 100
|
||||
|
||||
# 微层混合 event_20200326_cn
|
||||
return self._relative_image_color_hue_count(area=(-0.5, -0.2, 0.5, 0.2), h=(358 - 3, 358 + 3)) > 250
|
||||
|
@ -6,7 +6,7 @@ from module.map.map_grids import SelectedGrids, RoadGrids
|
||||
|
||||
|
||||
class Map(Fleet):
|
||||
def clear_chosen_enemy(self, grid):
|
||||
def clear_chosen_enemy(self, grid, expected=''):
|
||||
"""
|
||||
Args:
|
||||
grid (GridInfo):
|
||||
@ -15,9 +15,9 @@ class Map(Fleet):
|
||||
self.show_fleet()
|
||||
if self.config.ENABLE_EMOTION_REDUCE and self.config.ENABLE_MAP_FLEET_LOCK:
|
||||
self.emotion.wait()
|
||||
self.goto(grid, expected='combat')
|
||||
self.goto(grid, expected=f'combat_{expected}' if expected else 'combat')
|
||||
|
||||
self.full_scan(battle_count=self.battle_count, mystery_count=self.mystery_count)
|
||||
self.full_scan(battle_count=self.battle_count, mystery_count=self.mystery_count, siren_count=self.siren_count)
|
||||
self.find_path_initial()
|
||||
|
||||
def clear_chosen_mystery(self, grid):
|
||||
@ -261,12 +261,16 @@ class Map(Fleet):
|
||||
|
||||
def clear_siren(self):
|
||||
grids = self.map.select(may_siren=True, is_enemy=True, is_accessible=True)
|
||||
grids = grids.add(self.map.select(may_siren=True, is_siren=True, is_accessible=True))
|
||||
logger.info('May siren: %s' % self.map.select(may_siren=True))
|
||||
logger.info('May siren and is enemy: %s' % self.map.select(may_siren=True, is_enemy=True))
|
||||
logger.info('Is siren: %s' % self.map.select(is_siren=True))
|
||||
|
||||
if grids:
|
||||
logger.hr('Clear siren')
|
||||
grids = grids.sort(cost=True, weight=True)
|
||||
logger.info('Grids: %s' % str(grids))
|
||||
self.clear_chosen_enemy(grids[0])
|
||||
self.clear_chosen_enemy(grids[0], expected='siren')
|
||||
return True
|
||||
|
||||
return False
|
||||
|
@ -76,9 +76,18 @@ class CampaignMap:
|
||||
@shape.setter
|
||||
def shape(self, scale):
|
||||
self._shape = node2location(scale.upper())
|
||||
for y in range(self._shape[1] + 1):
|
||||
for x in range(self._shape[0] + 1):
|
||||
grid = GridInfo()
|
||||
grid.location = (x, y)
|
||||
self.grids[(x, y)] = grid
|
||||
|
||||
# camera_data can be generate automatically, but it's better to set it manually.
|
||||
self._camera_data = SelectedGrids(camera_2d(self._shape, sight=(-3, -1, 3, 2)))
|
||||
self.camera_data = [location2node(loca) for loca in camera_2d(self._shape, sight=(-3, -1, 3, 2))]
|
||||
|
||||
# weight_data set to 10.
|
||||
for grid in self:
|
||||
grid.weight = 10.
|
||||
|
||||
@property
|
||||
def map_data(self):
|
||||
@ -88,11 +97,7 @@ class CampaignMap:
|
||||
def map_data(self, text):
|
||||
self._map_data = text
|
||||
for loca, data in self._parse_text(text):
|
||||
grid = GridInfo()
|
||||
grid.location = loca
|
||||
grid.weight = 10. # weight_data can be generate automatically
|
||||
grid.decode(data)
|
||||
self.grids[loca] = grid
|
||||
self.grids[loca].decode(data)
|
||||
|
||||
def show(self):
|
||||
# logger.info('Showing grids:')
|
||||
@ -298,11 +303,12 @@ class CampaignMap:
|
||||
|
||||
return path
|
||||
|
||||
def missing_get(self, battle_count, mystery_count=0):
|
||||
def missing_get(self, battle_count, mystery_count=0, siren_count=0):
|
||||
missing = self.spawn_data[battle_count].copy()
|
||||
may = {'enemy': 0, 'mystery': 0, 'siren': 0,'boss': 0}
|
||||
may = {'enemy': 0, 'mystery': 0, 'siren': 0, 'boss': 0}
|
||||
missing['enemy'] -= battle_count
|
||||
missing['mystery'] -= mystery_count
|
||||
missing['siren'] -= siren_count
|
||||
for grid in self:
|
||||
for attr in may.keys():
|
||||
if grid.__getattribute__('is_' + attr):
|
||||
@ -321,8 +327,8 @@ class CampaignMap:
|
||||
logger.info('may: %s' % may)
|
||||
return may, missing
|
||||
|
||||
def missing_is_none(self, battle_count, mystery_count=0):
|
||||
may, missing = self.missing_get(battle_count, mystery_count)
|
||||
def missing_is_none(self, battle_count, mystery_count=0, siren_count=0):
|
||||
may, missing = self.missing_get(battle_count, mystery_count, siren_count)
|
||||
|
||||
for key in may.keys():
|
||||
if missing[key] != 0:
|
||||
@ -330,8 +336,8 @@ class CampaignMap:
|
||||
|
||||
return True
|
||||
|
||||
def missing_predict(self, battle_count, mystery_count=0):
|
||||
may, missing = self.missing_get(battle_count, mystery_count)
|
||||
def missing_predict(self, battle_count, mystery_count=0, siren_count=0):
|
||||
may, missing = self.missing_get(battle_count, mystery_count, siren_count)
|
||||
|
||||
# predict
|
||||
for grid in self:
|
||||
|
@ -38,7 +38,7 @@ class MapOperation(UrgentCommissionHandler, EnemySearchingHandler, FleetPreparat
|
||||
self.device.screenshot()
|
||||
|
||||
if not checked_in_map and self.is_in_map():
|
||||
logger.info('Already in map.')
|
||||
logger.info('Already in map, skip enter_map.')
|
||||
return False
|
||||
else:
|
||||
checked_in_map = True
|
||||
|
@ -111,7 +111,7 @@ class Perspective:
|
||||
)
|
||||
if len(horizontal) - len(self.horizontal) >= 3 or len(vertical) - len(self.vertical) >= 3:
|
||||
logger.warning('Too many deleted lines')
|
||||
# self.save_error_image()
|
||||
self.save_error_image()
|
||||
|
||||
def load_image(self, image):
|
||||
"""Method that turns image to monochrome and hide UI.
|
||||
|
Loading…
Reference in New Issue
Block a user