1
0
mirror of https://gitee.com/sui-feng-cb/AzurLaneAutoScript1 synced 2026-06-03 15:30:14 +08:00

Merge pull request #117 from LmeSzinc/dev

Opt: Different grid.cost for different fleet
This commit is contained in:
Kyo
2020-08-06 03:02:23 -03:00
committed by GitHub
22 changed files with 283 additions and 117 deletions

View File

@@ -49,7 +49,7 @@ class Campaign(CampaignBase, HardEquipment):
# logger.info('Grids: %s' % grids) # logger.info('Grids: %s' % grids)
if grids: if grids:
logger.hr('Clear BOSS') logger.hr('Clear BOSS')
grids = grids.sort(cost=True, weight=True) grids = grids.sort('weight', 'cost')
logger.info('Grids: %s' % str(grids)) logger.info('Grids: %s' % str(grids))
self._goto(grids[0], expected='boss') self._goto(grids[0], expected='boss')
raise CampaignEnd('BOSS Clear.') raise CampaignEnd('BOSS Clear.')

View File

@@ -44,7 +44,7 @@ A7, B7, C7, D7, E7, F7, G7, H7, I7, \
class Config: class Config:
SUBMARINE = 0 SUBMARINE = 0
POOR_MAP_DATA = True # POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True MAP_HAS_MOVABLE_ENEMY = True

View File

@@ -49,7 +49,7 @@ A8, B8, C8, D8, E8, F8, G8, H8, I8, \
class Config: class Config:
SUBMARINE = 0 SUBMARINE = 0
POOR_MAP_DATA = True # POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True MAP_HAS_MOVABLE_ENEMY = True

View File

@@ -45,7 +45,7 @@ A7, B7, C7, D7, E7, F7, G7, H7, I7, \
class Config: class Config:
SUBMARINE = 0 SUBMARINE = 0
POOR_MAP_DATA = True # POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True MAP_HAS_MOVABLE_ENEMY = True

View File

@@ -49,7 +49,7 @@ A8, B8, C8, D8, E8, F8, G8, H8, I8, \
class Config: class Config:
SUBMARINE = 0 SUBMARINE = 0
POOR_MAP_DATA = True # POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True MAP_HAS_MOVABLE_ENEMY = True

View File

@@ -57,7 +57,7 @@ A10, B10, C10, D10, E10, F10, G10, H10, I10, J10, K10, L10, M10, \
class Config: class Config:
SUBMARINE = 0 SUBMARINE = 0
POOR_MAP_DATA = True # POOR_MAP_DATA = True
MAP_HAS_AMBUSH = False MAP_HAS_AMBUSH = False
MAP_HAS_FLEET_STEP = True MAP_HAS_FLEET_STEP = True
MAP_HAS_MOVABLE_ENEMY = True MAP_HAS_MOVABLE_ENEMY = True

View File

@@ -1,7 +1,6 @@
from module.base.button import Button from module.base.button import Button
from module.logger import logger from module.logger import logger
from module.exception import CampaignEnd from module.exception import CampaignEnd, ScriptError, MapEnemyMoved
from module.exception import ScriptError
from module.map.map import Map from module.map.map import Map
from module.map.map_base import CampaignMap from module.map.map_base import CampaignMap
from module.base.decorator import Config from module.base.decorator import Config
@@ -27,9 +26,8 @@ class CampaignBase(Map):
return False return False
@Config.when(POOR_MAP_DATA=True, MAP_CLEAR_ALL_THIS_TIME=False) @Config.when(POOR_MAP_DATA=True, MAP_CLEAR_ALL_THIS_TIME=False)
def execute_a_battle(self): def battle_function(self):
logger.hr(f'{self.FUNCTION_NAME_BASE}{self.battle_count}', level=2) logger.info('Using function: battle_with_poor_map_data')
logger.info('Running with poor map data.')
if self.fleet_2_break_siren_caught(): if self.fleet_2_break_siren_caught():
return True return True
self.clear_all_mystery() self.clear_all_mystery()
@@ -45,12 +43,10 @@ class CampaignBase(Map):
return True return True
return self.clear_enemy() return self.clear_enemy()
logger.warning('No battle executed.')
return False return False
@Config.when(MAP_CLEAR_ALL_THIS_TIME=True) @Config.when(MAP_CLEAR_ALL_THIS_TIME=True)
def execute_a_battle(self): def battle_function(self):
logger.hr(f'{self.FUNCTION_NAME_BASE}{self.battle_count}', level=2)
logger.info('Using function: clear_all') logger.info('Using function: clear_all')
if self.fleet_2_break_siren_caught(): if self.fleet_2_break_siren_caught():
return True return True
@@ -60,7 +56,7 @@ class CampaignBase(Map):
self.pick_up_ammo() self.pick_up_ammo()
remain = self.map.select(is_enemy=True, is_boss=False) remain = self.map.select(is_enemy=True, is_boss=False)
logger.info('Enemy remain: {}') logger.info(f'Enemy remain: {remain}')
if remain.count > 0: if remain.count > 0:
if self.clear_siren(): if self.clear_siren():
return True return True
@@ -74,18 +70,35 @@ class CampaignBase(Map):
return result return result
@Config.when(MAP_CLEAR_ALL_THIS_TIME=False, POOR_MAP_DATA=False) @Config.when(MAP_CLEAR_ALL_THIS_TIME=False, POOR_MAP_DATA=False)
def execute_a_battle(self): def battle_function(self):
func = self.FUNCTION_NAME_BASE + 'default' func = self.FUNCTION_NAME_BASE + 'default'
for extra_battle in range(10): for extra_battle in range(10):
if hasattr(self, self.FUNCTION_NAME_BASE + str(self.battle_count - extra_battle)): if hasattr(self, self.FUNCTION_NAME_BASE + str(self.battle_count - extra_battle)):
func = self.FUNCTION_NAME_BASE + str(self.battle_count - extra_battle) func = self.FUNCTION_NAME_BASE + str(self.battle_count - extra_battle)
break break
logger.hr(f'{self.FUNCTION_NAME_BASE}{self.battle_count}', level=2)
logger.info(f'Using function: {func}') logger.info(f'Using function: {func}')
func = self.__getattribute__(func) func = self.__getattribute__(func)
result = func() result = func()
return result
def execute_a_battle(self):
logger.hr(f'{self.FUNCTION_NAME_BASE}{self.battle_count}', level=2)
prev = self.battle_count
result = False
for _ in range(10):
try:
result = self.battle_function()
break
except MapEnemyMoved:
if self.battle_count > prev:
result = True
break
else:
continue
if not result: if not result:
logger.warning('ScriptError, No combat executed.') logger.warning('ScriptError, No combat executed.')
if self.config.ENABLE_EXCEPTION: if self.config.ENABLE_EXCEPTION:
@@ -105,7 +118,7 @@ class CampaignBase(Map):
self.handle_fleet_reverse() self.handle_fleet_reverse()
self.map_init(self.MAP) self.map_init(self.MAP)
for _ in range(50): for _ in range(20):
try: try:
self.execute_a_battle() self.execute_a_battle()
except CampaignEnd: except CampaignEnd:

View File

@@ -53,6 +53,9 @@ class AzurLaneConfig:
USING_SPARE_FLEET = False USING_SPARE_FLEET = False
MOVABLE_ENEMY_FLEET_STEP = 2
MOVABLE_ENEMY_TURN = 2
@property @property
def FLEET_1(self): def FLEET_1(self):
return self.FLEET_3 if self.USING_SPARE_FLEET else self._FLEET_1 return self.FLEET_3 if self.USING_SPARE_FLEET else self._FLEET_1

View File

@@ -6,7 +6,7 @@ from retrying import retry
from module.base.timer import Timer from module.base.timer import Timer
from module.base.utils import * from module.base.utils import *
from module.device.connection import Connection from module.device.connection import Connection
from module.exception import ScriptError from module.exception import GameTooManyClickError
from module.logger import logger from module.logger import logger
@@ -36,7 +36,7 @@ class Control(Connection):
if sum([1 if str(prev) == str(button) else 0 for prev in self.click_record]) >= 12: if sum([1 if str(prev) == str(button) else 0 for prev in self.click_record]) >= 12:
logger.warning(f'Too many click for a button: {button}') logger.warning(f'Too many click for a button: {button}')
logger.info(f'History click: {[str(prev) for prev in self.click_record]}') logger.info(f'History click: {[str(prev) for prev in self.click_record]}')
raise ScriptError(f'Too many click for a button: {button}') raise GameTooManyClickError(f'Too many click for a button: {button}')
else: else:
self.click_record.append(str(button)) self.click_record.append(str(button))

View File

@@ -10,6 +10,10 @@ class MapWalkError(Exception):
pass pass
class MapEnemyMoved(Exception):
pass
class CampaignNameError(Exception): class CampaignNameError(Exception):
pass pass
@@ -26,5 +30,9 @@ class GameStuckError(Exception):
pass pass
class GameTooManyClickError(Exception):
pass
class GameNotRunningError(Exception): class GameNotRunningError(Exception):
pass pass

View File

@@ -99,6 +99,8 @@ class InfoHandler(ModuleBase):
if self.story_popup_timout.started() and not self.story_popup_timout.reached(): if self.story_popup_timout.started() and not self.story_popup_timout.reached():
if self.handle_popup_confirm('STORY_SKIP'): if self.handle_popup_confirm('STORY_SKIP'):
self.story_popup_timout = Timer(10) self.story_popup_timout = Timer(10)
self.interval_reset(STORY_SKIP)
self.interval_reset(STORY_LETTERS_ONLY)
return True return True
if self.appear_then_click(STORY_SKIP, offset=True, interval=2): if self.appear_then_click(STORY_SKIP, offset=True, interval=2):
self.story_popup_timout.reset() self.story_popup_timout.reset()

View File

@@ -2,6 +2,7 @@ from datetime import datetime
from module.base.timer import Timer from module.base.timer import Timer
from module.combat.combat import Combat from module.combat.combat import Combat
from module.exception import GameTooManyClickError, ScriptError
from module.handler.assets import * from module.handler.assets import *
from module.logger import logger from module.logger import logger
from module.map.assets import * from module.map.assets import *
@@ -10,7 +11,12 @@ from module.ui.ui import MAIN_CHECK
class LoginHandler(Combat): class LoginHandler(Combat):
def handle_app_login(self): def _handle_app_login(self):
"""
Pages:
in: Any page
out: page_main
"""
logger.hr('App login') logger.hr('App login')
confirm_timer = Timer(1.5, count=4).start() confirm_timer = Timer(1.5, count=4).start()
@@ -54,6 +60,27 @@ class LoginHandler(Combat):
self.config.start_time = datetime.now() self.config.start_time = datetime.now()
return True return True
def handle_app_login(self):
"""
Returns:
bool: If login success
Raises:
ScriptError: If login failed more than 3
"""
for _ in range(3):
try:
self._handle_app_login()
return True
except GameTooManyClickError as e:
logger.warning(e)
self.device.app_stop()
self.device.app_start()
continue
logger.warning('Login failed more than 3')
raise ScriptError('Login failed more than 3')
def app_restart(self): def app_restart(self):
logger.hr('App restart') logger.hr('App restart')
self.device.app_stop() self.device.app_stop()

View File

@@ -190,27 +190,32 @@ class Camera(InfoHandler):
if np.all(np.abs(vector) <= 0): if np.all(np.abs(vector) <= 0):
break break
def full_scan(self, battle_count=0, mystery_count=0, siren_count=0, carrier_count=0, mode='normal'):
"""Scan the hole map. def full_scan(self, queue=None, must_scan=None, battle_count=0, mystery_count=0, siren_count=0, carrier_count=0,
mode='normal'):
"""Scan the whole map.
Args: Args:
queue (SelectedGrids): Grids to focus on. If none, use map.camera_data
must_scan (SelectedGrids): Must scan these grids
battle_count: battle_count:
mystery_count: mystery_count:
siren_count: siren_count:
carrier_count: carrier_count:
mode (str): Scan mode, such as 'normal', 'carrier', 'move' mode (str): Scan mode, such as 'normal', 'carrier', 'movable'
""" """
logger.info('Full scan start') logger.info('Full scan start')
self.map.reset_fleet() self.map.reset_fleet()
queue = self.map.camera_data queue = queue if queue else self.map.camera_data
if battle_count == 0: if must_scan:
queue = queue.add(self.map.camera_data_spawn_point) queue = queue.add(must_scan)
while len(queue) > 0: while len(queue) > 0:
if self.map.missing_is_none(battle_count, mystery_count, siren_count, carrier_count): if self.map.missing_is_none(battle_count, mystery_count, siren_count, carrier_count):
if battle_count == 0 and queue.count != queue.delete(self.map.camera_data_spawn_point).count: if must_scan and queue.count != queue.delete(must_scan).count:
logger.info('Continue scanning spawn points.') logger.info('Continue scanning.')
pass pass
else: else:
logger.info('All spawn found, Early stopped.') logger.info('All spawn found, Early stopped.')
@@ -226,8 +231,8 @@ class Camera(InfoHandler):
queue = queue[1:] queue = queue[1:]
if battle_count is not None: self.map.missing_predict(battle_count, mystery_count, siren_count, carrier_count)
self.map.missing_predict(battle_count, mystery_count, siren_count, carrier_count)
self.map.show() self.map.show()

View File

@@ -3,7 +3,7 @@ import itertools
import numpy as np import numpy as np
from module.base.timer import Timer from module.base.timer import Timer
from module.exception import MapWalkError from module.exception import MapWalkError, MapEnemyMoved
from module.handler.ambush import AmbushHandler from module.handler.ambush import AmbushHandler
from module.logger import logger from module.logger import logger
from module.map.camera import Camera from module.map.camera import Camera
@@ -85,6 +85,71 @@ class Fleet(Camera, MapOperation, AmbushHandler):
def switch_to(self): def switch_to(self):
pass pass
round = 0
enemy_round = {}
def round_next(self):
"""
Call this method after fleet arrived.
"""
if not self.config.MAP_HAS_MOVABLE_ENEMY:
return False
self.round += 1
logger.info(f'Round: {self.round}, enemy_round: {self.enemy_round}')
def round_battle(self):
"""
Call this method after cleared an enemy.
"""
if not self.config.MAP_HAS_MOVABLE_ENEMY:
return False
if not self.map.select(is_siren=True):
self.enemy_round = {}
try:
data = self.map.spawn_data[self.battle_count]
except IndexError:
data = {}
enemy = data.get('siren', 0)
if enemy > 0:
r = self.round % self.config.MOVABLE_ENEMY_TURN
self.enemy_round[r] = self.enemy_round.get(r, 0) + enemy
def round_reset(self):
"""
Call this method after entering map.
"""
self.round = 0
self.enemy_round = {}
@property
def round_is_new(self):
"""
Usually, MOVABLE_ENEMY_TURN = 2.
So a walk round is `player - player - enemy`, player moves twice, enemy moves once.
Returns:
bool: If it's a new walk round, which means enemies have moved.
"""
if not self.config.MAP_HAS_MOVABLE_ENEMY:
return False
r = self.round % self.config.MOVABLE_ENEMY_TURN
if r in self.enemy_round:
logger.info('Enemy moved')
return True
else:
return False
@property
def round_wait(self):
"""
Returns:
float: Seconds to wait enemies moving.
"""
if not self.config.MAP_HAS_MOVABLE_ENEMY:
return 0
r = (self.round + 1) % self.config.MOVABLE_ENEMY_TURN
return self.enemy_round.get(r, 0) * self.config.MAP_SIREN_MOVE_WAIT
def _goto(self, location, expected=''): def _goto(self, location, expected=''):
"""Goto a grid directly and handle ambush, air raid, mystery picked up, combat. """Goto a grid directly and handle ambush, air raid, mystery picked up, combat.
@@ -92,7 +157,6 @@ class Fleet(Camera, MapOperation, AmbushHandler):
location (tuple, str, GridInfo): Destination. location (tuple, str, GridInfo): Destination.
""" """
location = location_ensure(location) location = location_ensure(location)
siren_count = self.map.select(is_siren=True).count
result_mystery = '' result_mystery = ''
while 1: while 1:
@@ -108,10 +172,8 @@ class Fleet(Camera, MapOperation, AmbushHandler):
self.device.click(grid) self.device.click(grid)
arrived = False arrived = False
# Wait to confirm fleet arrived. It does't appear immediately if fleet in combat . # Wait to confirm fleet arrived. It does't appear immediately if fleet in combat .
add = self.config.MAP_SIREN_MOVE_WAIT * min(self.config.MAP_SIREN_COUNT, siren_count) \ arrive_timer = Timer(0.5 + self.round_wait, count=2)
if self.config.MAP_HAS_MOVABLE_ENEMY and not self.config.ENABLE_FAST_FORWARD else 0 arrive_unexpected_timer = Timer(1.5 + self.round_wait, count=6)
arrive_timer = Timer(0.5 + add, count=2)
arrive_unexpected_timer = Timer(1.5 + add, count=6)
# Wait after ambushed. # Wait after ambushed.
ambushed_retry = Timer(0.5) ambushed_retry = Timer(0.5)
# If nothing happens, click again. # If nothing happens, click again.
@@ -125,6 +187,7 @@ class Fleet(Camera, MapOperation, AmbushHandler):
# Ambush # Ambush
if self.handle_ambush(): if self.handle_ambush():
ambushed_retry.start() ambushed_retry.start()
walk_timeout.reset()
# Mystery # Mystery
mystery = self.handle_mystery(button=grid) mystery = self.handle_mystery(button=grid)
@@ -149,7 +212,7 @@ class Fleet(Camera, MapOperation, AmbushHandler):
result = 'combat' result = 'combat'
self.battle_count += 1 self.battle_count += 1
self.fleet_ammo -= 1 self.fleet_ammo -= 1
if 'siren' in expected: if 'siren' in expected or (self.config.MAP_HAS_MOVABLE_ENEMY and not expected):
self.siren_count += 1 self.siren_count += 1
elif self.map[location].may_enemy: elif self.map[location].may_enemy:
self.map[location].is_cleared = True self.map[location].is_cleared = True
@@ -205,7 +268,14 @@ class Fleet(Camera, MapOperation, AmbushHandler):
self.map[location].is_fleet = True self.map[location].is_fleet = True
self.__setattr__('fleet_%s_location' % self.fleet_current_index, location) self.__setattr__('fleet_%s_location' % self.fleet_current_index, location)
if result_mystery == 'get_carrier': if result_mystery == 'get_carrier':
self.full_scan(mode='carrier') self.full_scan_carrier()
if result == 'combat':
self.round_battle()
self.round_next()
if self.round_is_new:
self.full_scan_movable()
self.find_path_initial()
raise MapEnemyMoved
self.find_path_initial() self.find_path_initial()
def goto(self, location, optimize=True, expected=''): def goto(self, location, optimize=True, expected=''):
@@ -225,20 +295,20 @@ class Fleet(Camera, MapOperation, AmbushHandler):
else: else:
self._goto(location, expected=expected) self._goto(location, expected=expected)
def find_path_initial(self, grid=None): def find_path_initial(self):
""" """
Args: Call this method after fleet moved or entered map.
grid (tuple, GridInfo): Current fleet grid
""" """
if grid is None:
grid = self.fleet_current
else:
grid = location_ensure(grid)
if self.fleet_1_location: if self.fleet_1_location:
self.map[self.fleet_1_location].is_fleet = True self.map[self.fleet_1_location].is_fleet = True
if self.fleet_2_location: if self.fleet_2_location:
self.map[self.fleet_2_location].is_fleet = True self.map[self.fleet_2_location].is_fleet = True
self.map.find_path_initial(grid, has_ambush=self.config.MAP_HAS_AMBUSH) location_dict = {}
if self.config.FLEET_2:
location_dict[2] = self.fleet_2_location
location_dict[1] = self.fleet_1_location
self.map.find_path_initial_multi_fleet(
location_dict, current=self.fleet_current, has_ambush=self.config.MAP_HAS_AMBUSH)
def show_fleet(self): def show_fleet(self):
fleets = [] fleets = []
@@ -251,15 +321,10 @@ class Fleet(Camera, MapOperation, AmbushHandler):
fleets.append(text) fleets.append(text)
logger.info(' '.join(fleets)) logger.info(' '.join(fleets))
def full_scan(self, mode='normal'): def full_scan(self, queue=None, must_scan=None, mode='normal'):
prev_enemy = self.map.select(is_enemy=True) super().full_scan(
super().full_scan(battle_count=self.battle_count, mystery_count=self.mystery_count, queue=queue, must_scan=must_scan, battle_count=self.battle_count, mystery_count=self.mystery_count,
siren_count=self.siren_count, carrier_count=self.carrier_count, siren_count=self.siren_count, carrier_count=self.carrier_count, mode=mode)
mode=mode)
if mode == 'carrier':
diff = self.map.select(is_enemy=True).delete(prev_enemy)
logger.info(f'Carrier spawn: {diff}')
if self.config.FLEET_2 and not self.fleet_2_location: if self.config.FLEET_2 and not self.fleet_2_location:
fleets = self.map.select(is_fleet=True, is_current_fleet=False) fleets = self.map.select(is_fleet=True, is_current_fleet=False)
@@ -276,6 +341,19 @@ class Fleet(Camera, MapOperation, AmbushHandler):
else: else:
self.map[loca].wipe_out() self.map[loca].wipe_out()
def full_scan_carrier(self):
prev = self.map.select(is_enemy=True)
self.full_scan(mode='carrier')
diff = self.map.select(is_enemy=True).delete(prev)
logger.info(f'Carrier spawn: {diff}')
def full_scan_movable(self):
prev = self.map.select(is_siren=True)
for grid in prev:
grid.wipe_out()
self.full_scan(queue=prev, must_scan=prev, mode='movable')
# after = self.map.select(is_siren=True)
def find_all_fleets(self): def find_all_fleets(self):
logger.hr('Find all fleets') logger.hr('Find all fleets')
queue = self.map.select(is_spawn_point=True) queue = self.map.select(is_spawn_point=True)
@@ -373,20 +451,23 @@ class Fleet(Camera, MapOperation, AmbushHandler):
self.hp_init() self.hp_init()
self.handle_strategy(index=self.fleet_current_index) self.handle_strategy(index=self.fleet_current_index)
self.ensure_edge_insight(preset=self.map.in_map_swipe_preset_data) self.ensure_edge_insight(preset=self.map.in_map_swipe_preset_data)
self.full_scan() self.full_scan(must_scan=self.map.camera_data_spawn_point)
self.find_current_fleet() self.find_current_fleet()
self.find_path_initial() self.find_path_initial()
self.map.show_cost() self.map.show_cost()
self.round_reset()
self.round_battle()
def handle_map_green_config_cover(self): def handle_map_green_config_cover(self):
if not self.is_map_green: if not self.is_map_green:
return False return False
logger.info('Map is green sea.') logger.info('Map is green sea.')
self.config.MAP_HAS_FLEET_STEP = False
self.config.MAP_HAS_MOVABLE_ENEMY = False
if self.config.ENABLE_FAST_FORWARD: if self.config.ENABLE_FAST_FORWARD:
self.config.MAP_HAS_AMBUSH = False self.config.MAP_HAS_AMBUSH = False
self.config.MAP_HAS_FLEET_STEP = False
self.config.MAP_HAS_MOVABLE_ENEMY = False
else: else:
# When disable fast forward, MAP_HAS_AMBUSH depends on map settings. # When disable fast forward, MAP_HAS_AMBUSH depends on map settings.
# self.config.MAP_HAS_AMBUSH = True # self.config.MAP_HAS_AMBUSH = True
@@ -397,7 +478,7 @@ class Fleet(Camera, MapOperation, AmbushHandler):
return True return True
def _expected_combat_end(self, expected): def _expected_combat_end(self, expected):
for data in self.map._spawn_data_backup: for data in self.map.spawn_data:
if data.get('battle') == self.battle_count and 'boss' in expected: if data.get('battle') == self.battle_count and 'boss' in expected:
return 'in_stage' return 'in_stage'
if data.get('battle') == self.battle_count + 1: if data.get('battle') == self.battle_count + 1:

View File

@@ -49,6 +49,8 @@ class GridInfo:
is_ambush_save = False is_ambush_save = False
is_caught_by_siren = False is_caught_by_siren = False
cost = 9999 cost = 9999
cost_1 = 9999
cost_2 = 9999
connection = None connection = None
weight = 1 weight = 1
@@ -128,6 +130,14 @@ class GridInfo:
def is_accessible(self): def is_accessible(self):
return self.cost < 9999 return self.cost < 9999
@property
def is_accessible_1(self):
return self.cost_1 < 9999
@property
def is_accessible_2(self):
return self.cost_2 < 9999
@property @property
def is_nearby(self): def is_nearby(self):
return self.cost < 20 return self.cost < 20

View File

@@ -60,7 +60,7 @@ class Map(Fleet):
@staticmethod @staticmethod
def select_grids(grids, nearby=False, is_accessible=True, scale=(), genre=(), strongest=False, weakest=False, def select_grids(grids, nearby=False, is_accessible=True, scale=(), genre=(), strongest=False, weakest=False,
cost=True, weight=True, ignore=None): sort=('weight', 'cost'), ignore=None):
""" """
Args: Args:
grids (SelectedGrids): grids (SelectedGrids):
@@ -70,8 +70,7 @@ class Map(Fleet):
genre (tuple[str], list[str]): light, main, carrier, treasure. (Case insensitive). genre (tuple[str], list[str]): light, main, carrier, treasure. (Case insensitive).
strongest (bool): strongest (bool):
weakest (bool): weakest (bool):
cost (bool): sort (tuple(str)):
weight (bool):
ignore (SelectedGrids): ignore (SelectedGrids):
Returns: Returns:
@@ -111,7 +110,7 @@ class Map(Fleet):
break break
if grids: if grids:
grids = grids.sort(cost=cost, weight=weight) grids = grids.sort(*sort)
return grids return grids
@@ -247,7 +246,7 @@ class Map(Fleet):
if grids: if grids:
logger.hr('Clear BOSS') logger.hr('Clear BOSS')
grids = grids.sort(cost=True, weight=True) grids = grids.sort('weight', 'cost')
logger.info('Grids: %s' % str(grids)) logger.info('Grids: %s' % str(grids))
self.clear_chosen_enemy(grids[0]) self.clear_chosen_enemy(grids[0])
@@ -274,7 +273,7 @@ class Map(Fleet):
if grids: if grids:
logger.hr('Clear BOSS') logger.hr('Clear BOSS')
grids = grids.sort(cost=True, weight=True) grids = grids.sort('weight', 'cost')
logger.info('Grids: %s' % str(grids)) logger.info('Grids: %s' % str(grids))
self.clear_chosen_enemy(grids[0]) self.clear_chosen_enemy(grids[0])
@@ -291,7 +290,7 @@ class Map(Fleet):
for grid in grids: for grid in grids:
logger.hr('Clear potential BOSS') logger.hr('Clear potential BOSS')
grids = grids.sort(cost=True, weight=True) grids = grids.sort('weight', 'cost')
logger.info('Grid: %s' % str(grid)) logger.info('Grid: %s' % str(grid))
self.clear_chosen_enemy(grid) self.clear_chosen_enemy(grid)
if self.battle_count > battle_count: if self.battle_count > battle_count:
@@ -316,7 +315,7 @@ class Map(Fleet):
if self.brute_fleet_meet(): if self.brute_fleet_meet():
return True return True
logger.info('Brute clear BOSS roadblocks') logger.info('Brute clear BOSS roadblocks')
grids = grids.sort(cost=True, weight=True) grids = grids.sort('weight', 'cost')
logger.info('Grids: %s' % str(grids)) logger.info('Grids: %s' % str(grids))
self.clear_chosen_enemy(grids[0]) self.clear_chosen_enemy(grids[0])
return True return True
@@ -339,7 +338,7 @@ class Map(Fleet):
grids = self.brute_find_roadblocks(self.map[self.fleet_2_location], fleet=1) grids = self.brute_find_roadblocks(self.map[self.fleet_2_location], fleet=1)
if grids: if grids:
logger.info('Brute clear roadblocks between fleets.') logger.info('Brute clear roadblocks between fleets.')
grids = grids.sort(cost=True, weight=True) grids = grids.sort('weight', 'cost')
logger.info('Grids: %s' % str(grids)) logger.info('Grids: %s' % str(grids))
self.clear_chosen_enemy(grids[0]) self.clear_chosen_enemy(grids[0])
return True return True
@@ -354,20 +353,9 @@ class Map(Fleet):
if not self.config.MAP_HAS_SIREN: if not self.config.MAP_HAS_SIREN:
return False return False
logger.info('May siren: %s' % self.map.select(may_siren=True)) if self.config.FLEET_2:
logger.info('May siren and is enemy: %s' % self.map.select(may_siren=True, is_enemy=True)) kwargs['sort'] = ('weight', 'cost_2')
grids = self.map.select(may_siren=True, is_enemy=True) grids = self.map.select(is_siren=True)
logger.info('Is siren: %s' % self.map.select(is_siren=True))
grids = grids.add(self.map.select(is_siren=True))
if self.config.POOR_MAP_DATA or not self.is_map_green:
logger.info('Is 0 scale enemy: %s' % self.map.select(is_enemy=True, enemy_scale=0))
grids = grids.add(self.map.select(is_enemy=True, enemy_scale=0))
logger.info('Delete is boss: %s' % self.map.select(is_boss=True))
grids = grids.delete(self.map.select(is_boss=True))
grids = self.select_grids(grids, **kwargs) grids = self.select_grids(grids, **kwargs)
if grids: if grids:
@@ -453,15 +441,13 @@ class Map(Fleet):
return False return False
logger.info('Fleet_2 push forward') logger.info('Fleet_2 push forward')
grids = self.map.select(is_land=False).sort(cost=True, weight=True) grids = self.map.select(is_land=False).sort('weight', 'cost')
if self.map[self.fleet_2_location].weight <= grids[0].weight: if self.map[self.fleet_2_location].weight <= grids[0].weight:
logger.info('Fleet_2 pushed to destination') logger.info('Fleet_2 pushed to destination')
return False return False
self.find_path_initial(self.fleet_2_location)
fleets = SelectedGrids([self.map[self.fleet_1_location], self.map[self.fleet_2_location]]) fleets = SelectedGrids([self.map[self.fleet_1_location], self.map[self.fleet_2_location]])
grids = grids.select(is_accessible=True, is_sea=True).delete(fleets) grids = grids.select(is_accessible_2=True, is_sea=True).delete(fleets)
self.find_path_initial()
if not grids: if not grids:
logger.info('Fleet_2 has no where to push') logger.info('Fleet_2 has no where to push')
return False return False

View File

@@ -45,7 +45,7 @@ class CampaignMap:
self._wall_data = '' self._wall_data = ''
self._block_data = [] self._block_data = []
self._spawn_data = [] self._spawn_data = []
self._spawn_data_backup = [] self._spawn_data_stack = []
self._camera_data = [] self._camera_data = []
self._camera_data_spawn_point = [] self._camera_data_spawn_point = []
self.in_map_swipe_preset_data = None self.in_map_swipe_preset_data = None
@@ -177,7 +177,7 @@ class CampaignMap:
Args: Args:
grids: grids:
camera (tuple): camera (tuple):
mode (str): Scan mode, such as 'normal', 'carrier', 'move' mode (str): Scan mode, such as 'normal', 'carrier', 'movable'
""" """
offset = np.array(camera) - np.array(grids.center_loca) offset = np.array(camera) - np.array(grids.center_loca)
grids.show() grids.show()
@@ -247,7 +247,7 @@ class CampaignMap:
@spawn_data.setter @spawn_data.setter
def spawn_data(self, data_list): def spawn_data(self, data_list):
self._spawn_data_backup = data_list self._spawn_data = data_list
spawn = {'battle': 0, 'enemy': 0, 'mystery': 0, 'siren': 0, 'boss': 0} spawn = {'battle': 0, 'enemy': 0, 'mystery': 0, 'siren': 0, 'boss': 0}
for data in data_list: for data in data_list:
spawn['battle'] = data['battle'] spawn['battle'] = data['battle']
@@ -255,7 +255,11 @@ class CampaignMap:
spawn['mystery'] += data.get('mystery', 0) spawn['mystery'] += data.get('mystery', 0)
spawn['siren'] += data.get('siren', 0) spawn['siren'] += data.get('siren', 0)
spawn['boss'] += data.get('boss', 0) spawn['boss'] += data.get('boss', 0)
self._spawn_data.append(spawn.copy()) self._spawn_data_stack.append(spawn.copy())
@property
def spawn_data_stack(self):
return self._spawn_data_stack
@property @property
def weight_data(self): def weight_data(self):
@@ -271,7 +275,7 @@ class CampaignMap:
def is_map_data_poor(self): def is_map_data_poor(self):
if not self.select(may_enemy=True) or not self.select(may_boss=True) or not self.select(is_spawn_point=True): if not self.select(may_enemy=True) or not self.select(may_boss=True) or not self.select(is_spawn_point=True):
return False return False
if not len(self._spawn_data_backup): if not len(self.spawn_data):
return False return False
return True return True
@@ -291,8 +295,12 @@ class CampaignMap:
logger.info(text) logger.info(text)
def find_path_initial(self, location, has_ambush=True): def find_path_initial(self, location, has_ambush=True):
"""
Args:
location (tuple(int)): Grid location
has_ambush (bool): MAP_HAS_AMBUSH
"""
location = location_ensure(location) location = location_ensure(location)
ambush_cost = 10 if has_ambush else 1 ambush_cost = 10 if has_ambush else 1
for grid in self: for grid in self:
grid.cost = 9999 grid.cost = 9999
@@ -326,9 +334,22 @@ class CampaignMap:
# self.show_cost() # self.show_cost()
# self.show_connection() # self.show_connection()
def find_path_initial_multi_fleet(self, location_dict, current, has_ambush):
"""
Args:
location_dict (dict): Key: int, fleet index. Value: tuple(int), grid location.
current (tuple): Current location.
has_ambush (bool): MAP_HAS_AMBUSH
"""
location_dict = sorted(location_dict.items(), key=lambda kv: (int(kv[1] == current),))
for fleet, location in location_dict:
self.find_path_initial(location, has_ambush=has_ambush)
attr = f'cost_{fleet}'
for grid in self:
grid.__setattr__(attr, grid.cost)
def _find_path(self, location): def _find_path(self, location):
""" """
Args: Args:
location (tuple): location (tuple):
@@ -364,7 +385,6 @@ class CampaignMap:
def _find_route_node(self, route, step=0): def _find_route_node(self, route, step=0):
""" """
Args: Args:
route (list[tuple]): list of grids. route (list[tuple]): list of grids.
step (int): Fleet step in event map. Default to 0. step (int): Fleet step in event map. Default to 0.
@@ -443,9 +463,9 @@ class CampaignMap:
def missing_get(self, battle_count, mystery_count=0, siren_count=0, carrier_count=0): def missing_get(self, battle_count, mystery_count=0, siren_count=0, carrier_count=0):
try: try:
missing = self.spawn_data[battle_count].copy() missing = self.spawn_data_stack[battle_count].copy()
except IndexError: except IndexError:
missing = self.spawn_data[-1].copy() missing = self.spawn_data_stack[-1].copy()
may = {'enemy': 0, 'mystery': 0, 'siren': 0, 'boss': 0, 'carrier': 0} may = {'enemy': 0, 'mystery': 0, 'siren': 0, 'boss': 0, 'carrier': 0}
missing['enemy'] -= battle_count - siren_count missing['enemy'] -= battle_count - siren_count
missing['mystery'] -= mystery_count missing['mystery'] -= mystery_count
@@ -453,7 +473,7 @@ class CampaignMap:
missing['carrier'] = carrier_count - self.select(is_enemy=True, may_enemy=False).count missing['carrier'] = carrier_count - self.select(is_enemy=True, may_enemy=False).count
for grid in self: for grid in self:
for attr in ['enemy', 'mystery', 'siren', 'boss']: for attr in ['enemy', 'mystery', 'siren', 'boss']:
if grid.__getattribute__('is_' + attr) and grid.__getattribute__('may_' + attr): if grid.__getattribute__('is_' + attr):
missing[attr] -= 1 missing[attr] -= 1
for grid in self: for grid in self:
@@ -521,7 +541,6 @@ class CampaignMap:
def flatten(self): def flatten(self):
""" """
Returns: Returns:
list[GridInfo]: list[GridInfo]:
""" """

View File

@@ -105,23 +105,16 @@ class SelectedGrids:
g = [grid for grid in self.grids if grid not in grids] g = [grid for grid in self.grids if grid not in grids]
return SelectedGrids(g) return SelectedGrids(g)
def sort(self, cost=True, weight=True): def sort(self, *args):
""" """
Args: Args:
cost (bool): args (str): Attribute name to sort.
weight (bool):
Returns: Returns:
SelectedGrids:
""" """
attr = [] if len(args):
if weight: grids = sorted(self.grids, key=operator.attrgetter(*args))
attr.append('weight')
if cost:
attr.append('cost')
if len(attr):
grids = sorted(self.grids, key=operator.attrgetter(*attr))
return SelectedGrids(grids) return SelectedGrids(grids)
else: else:
return self return self

View File

@@ -141,7 +141,7 @@ class GridInfo:
""" """
Args: Args:
info (GridInfo): info (GridInfo):
mode (str): Scan mode, such as 'normal', 'carrier', 'move' mode (str): Scan mode, such as 'normal', 'carrier', 'movable'
Returns: Returns:
bool: If success. bool: If success.
@@ -166,6 +166,11 @@ class GridInfo:
self.enemy_scale = 0 self.enemy_scale = 0
self.enemy_genre = info.enemy_genre self.enemy_genre = info.enemy_genre
return True return True
elif mode == 'movable' and not self.is_land:
self.is_siren = True
self.enemy_scale = 0
self.enemy_genre = info.enemy_genre
return True
else: else:
return False return False
if info.is_enemy: if info.is_enemy:
@@ -173,7 +178,8 @@ class GridInfo:
self.is_enemy = True self.is_enemy = True
if info.enemy_scale: if info.enemy_scale:
self.enemy_scale = info.enemy_scale self.enemy_scale = info.enemy_scale
if info.enemy_genre != 'Enemy': if info.enemy_genre:
self.enemy_genre = info.enemy_genre self.enemy_genre = info.enemy_genre
return True return True
elif mode == 'carrier' and not self.is_land and self.may_carrier: elif mode == 'carrier' and not self.is_land and self.may_carrier:
@@ -181,7 +187,7 @@ class GridInfo:
self.is_carrier = True self.is_carrier = True
if info.enemy_scale: if info.enemy_scale:
self.enemy_scale = info.enemy_scale self.enemy_scale = info.enemy_scale
if info.enemy_genre != 'Enemy': if info.enemy_genre:
self.enemy_genre = info.enemy_genre self.enemy_genre = info.enemy_genre
return True return True
else: else:

View File

@@ -196,11 +196,19 @@ class ResearchSelector(UI):
out.append(index) out.append(index)
continue continue
proj = self.projects[index] proj = self.projects[index]
if not proj.valid:
continue
if (not self.config.RESEARCH_USE_CUBE and proj.need_cube) \ if (not self.config.RESEARCH_USE_CUBE and proj.need_cube) \
or (not self.config.RESEARCH_USE_COIN and proj.need_coin) \ or (not self.config.RESEARCH_USE_COIN and proj.need_coin) \
or (not self.config.RESEARCH_USE_PART and proj.need_part) \ or (not self.config.RESEARCH_USE_PART and proj.need_part):
or not proj.valid \ continue
or proj.genre.upper() in ['B', 'E']: # Reasons to ignore B series and E-2:
# - Can't guarantee research condition satisfied.
# You may get nothing after a day of running, because you didn't complete the precondition.
# - Low income from B series research.
# Gold B-4 basically equivalent to C-12, but needs a lot of oil.
if (proj.genre.upper() == 'B') \
or (proj.genre.upper() == 'E' and str(proj.duration) != '6'):
continue continue
out.append(index) out.append(index)
return out return out

View File

@@ -237,6 +237,7 @@ class RewardResearch(ResearchSelector):
if skip_first_screenshot: if skip_first_screenshot:
skip_first_screenshot = False skip_first_screenshot = False
else: else:
self.device.sleep(2)
self.device.screenshot() self.device.screenshot()
if self.appear(RESEARCH_CHECK, interval=10): if self.appear(RESEARCH_CHECK, interval=10):

View File

@@ -220,6 +220,7 @@ class Reward(RewardCommission, RewardTacticalClass, RewardResearch, LoginHandler
_enable_daily_reward = False _enable_daily_reward = False
_fleet_auto_mode = ('combat_auto', 'combat_auto', 'combat_auto') _fleet_auto_mode = ('combat_auto', 'combat_auto', 'combat_auto')
_enable_map_fleet_lock = False
def reward_backup_daily_reward_settings(self): def reward_backup_daily_reward_settings(self):
""" """
@@ -229,7 +230,10 @@ class Reward(RewardCommission, RewardTacticalClass, RewardResearch, LoginHandler
self.config.ENABLE_DAILY_REWARD = False self.config.ENABLE_DAILY_REWARD = False
self._fleet_auto_mode = self.config.FLEET_1_AUTO_MODE, self.config.FLEET_2_AUTO_MODE, self.config.FLEET_3_AUTO_MODE self._fleet_auto_mode = self.config.FLEET_1_AUTO_MODE, self.config.FLEET_2_AUTO_MODE, self.config.FLEET_3_AUTO_MODE
self.config.FLEET_1_AUTO_MODE, self.config.FLEET_2_AUTO_MODE, self.config.FLEET_3_AUTO_MODE = ('combat_auto', 'combat_auto', 'combat_auto') self.config.FLEET_1_AUTO_MODE, self.config.FLEET_2_AUTO_MODE, self.config.FLEET_3_AUTO_MODE = ('combat_auto', 'combat_auto', 'combat_auto')
self._enable_map_fleet_lock = self.config.ENABLE_MAP_FLEET_LOCK
self.config.ENABLE_MAP_FLEET_LOCK = True
def reward_recover_daily_reward_settings(self): def reward_recover_daily_reward_settings(self):
self.config.ENABLE_DAILY_REWARD = self._enable_daily_reward self.config.ENABLE_DAILY_REWARD = self._enable_daily_reward
self.config.FLEET_1_AUTO_MODE, self.config.FLEET_2_AUTO_MODE, self.config.FLEET_3_AUTO_MODE = self._fleet_auto_mode self.config.FLEET_1_AUTO_MODE, self.config.FLEET_2_AUTO_MODE, self.config.FLEET_3_AUTO_MODE = self._fleet_auto_mode
self.config.ENABLE_MAP_FLEET_LOCK = self._enable_map_fleet_lock