Add: 活动AB图每日三倍基本稳定

- 调整了微层混合透视识别的参数
- 增加了在hsv色彩空间下的识别方法
- 增加了对地面闪烁红框的识别
- 增加了已打精英的计数
- 修改精英识别, boss识别, 舰队识别方法为hsv颜色计数
- 修复了自动生成camera_data的报错
This commit is contained in:
LmeSzinc 2020-03-30 12:27:18 +08:00 committed by LmeSzinc
parent ce4887a4f5
commit 8366d7a258
14 changed files with 135 additions and 65 deletions

View File

@ -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()

View File

@ -50,3 +50,6 @@ class Campaign(CampaignBase):
return True
return self.battle_default()
def battle_4(self):
return self.clear_boss()

View File

@ -57,3 +57,6 @@ class Campaign(CampaignBase):
return True
return self.battle_default()
def battle_4(self):
return self.clear_boss()

View File

@ -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()

View File

@ -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()

View File

@ -196,6 +196,7 @@ class AzurLaneConfig:
'height': (255 - 24, 255),
'prominence': 10,
'distance': 50,
'width': (0, 7),
'wlen': 1000
}
# Parameters for cv2.HoughLines

View File

@ -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):

View File

@ -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)):

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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.