2023-04-05 12:30:06 +08:00
|
|
|
from datetime import timedelta
|
2021-09-23 22:04:03 +08:00
|
|
|
from module.config.utils import get_server_last_update
|
2020-03-29 01:22:46 +08:00
|
|
|
from module.exercise.assets import *
|
|
|
|
from module.exercise.combat import ExerciseCombat
|
|
|
|
from module.logger import logger
|
2023-04-09 09:09:49 +08:00
|
|
|
from module.ocr.ocr import Digit, Ocr, OcrYuv
|
2023-07-02 15:24:40 +08:00
|
|
|
from module.ui.page import page_exercise
|
2023-04-03 16:34:56 +08:00
|
|
|
|
2020-03-29 01:22:46 +08:00
|
|
|
|
2023-04-09 09:09:49 +08:00
|
|
|
class DatedDuration(Ocr):
|
|
|
|
def __init__(self, buttons, lang='cnocr', letter=(255, 255, 255), threshold=128, alphabet='0123456789:IDS天日d',
|
|
|
|
name=None):
|
|
|
|
super().__init__(buttons, lang=lang, letter=letter, threshold=threshold, alphabet=alphabet, name=name)
|
|
|
|
|
|
|
|
def after_process(self, result):
|
|
|
|
result = super().after_process(result)
|
|
|
|
result = result.replace('I', '1').replace('D', '0').replace('S', '5')
|
|
|
|
return result
|
2023-04-11 22:08:38 +08:00
|
|
|
|
2023-04-09 09:09:49 +08:00
|
|
|
def ocr(self, image, direct_ocr=False):
|
|
|
|
"""
|
|
|
|
Do OCR on a dated duration, such as `10d 01:30:30` or `7日01:30:30`.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
image:
|
|
|
|
direct_ocr:
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
list, datetime.timedelta: timedelta object, or a list of it.
|
|
|
|
"""
|
|
|
|
result_list = super().ocr(image, direct_ocr=direct_ocr)
|
|
|
|
if not isinstance(result_list, list):
|
|
|
|
result_list = [result_list]
|
|
|
|
result_list = [self.parse_time(result) for result in result_list]
|
|
|
|
if len(self.buttons) == 1:
|
|
|
|
result_list = result_list[0]
|
|
|
|
return result_list
|
2023-04-11 22:08:38 +08:00
|
|
|
|
2023-04-09 09:09:49 +08:00
|
|
|
@staticmethod
|
|
|
|
def parse_time(string):
|
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
string (str): `10d 01:30:30` or `7日01:30:30`
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
datetime.timedelta:
|
|
|
|
"""
|
|
|
|
import re
|
|
|
|
result = re.search(r'(\d{1,2})\D?(\d{1,2}):?(\d{2}):?(\d{2})', string)
|
|
|
|
if result:
|
|
|
|
result = [int(s) for s in result.groups()]
|
|
|
|
return timedelta(days=result[0], hours=result[1], minutes=result[2], seconds=result[3])
|
|
|
|
else:
|
|
|
|
logger.warning(f'Invalid dated duration: {string}')
|
|
|
|
return timedelta(days=0, hours=0, minutes=0, seconds=0)
|
2023-04-11 22:08:38 +08:00
|
|
|
|
2023-04-09 09:09:49 +08:00
|
|
|
|
|
|
|
class DatedDurationYuv(DatedDuration, OcrYuv):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2020-07-15 04:33:39 +08:00
|
|
|
OCR_EXERCISE_REMAIN = Digit(OCR_EXERCISE_REMAIN, letter=(173, 247, 74), threshold=128)
|
2023-04-05 12:30:06 +08:00
|
|
|
OCR_PERIOD_REMAIN = DatedDuration(OCR_PERIOD_REMAIN, letter=(255, 255, 255), threshold=128)
|
|
|
|
ADMIRAL_TRIAL_HOUR_INTERVAL = {
|
2023-04-09 12:52:35 +08:00
|
|
|
# "aggressive": [336, 0]
|
2023-04-05 12:30:06 +08:00
|
|
|
"sun18": [6, 0],
|
|
|
|
"sun12": [12, 6],
|
|
|
|
"sun0": [24, 12],
|
|
|
|
"sat18": [30, 24],
|
|
|
|
"sat12": [36, 30],
|
|
|
|
"sat0": [48, 36],
|
|
|
|
"fri18": [56, 48]
|
|
|
|
}
|
2020-03-29 01:22:46 +08:00
|
|
|
|
2023-04-09 09:09:49 +08:00
|
|
|
|
2020-03-29 01:22:46 +08:00
|
|
|
class Exercise(ExerciseCombat):
|
|
|
|
opponent_change_count = 0
|
2020-07-20 07:42:06 +08:00
|
|
|
remain = 0
|
2023-04-09 12:52:35 +08:00
|
|
|
preserve = 0
|
|
|
|
|
2020-03-29 01:22:46 +08:00
|
|
|
def _new_opponent(self):
|
|
|
|
logger.info('New opponent')
|
|
|
|
self.appear_then_click(NEW_OPPONENT)
|
|
|
|
self.opponent_change_count += 1
|
2020-07-20 20:37:54 +08:00
|
|
|
|
|
|
|
logger.attr("Change_opponent_count", self.opponent_change_count)
|
2021-09-25 23:29:10 +08:00
|
|
|
self.config.set_record(Exercise_OpponentRefreshValue=self.opponent_change_count)
|
2020-07-20 20:37:54 +08:00
|
|
|
|
2020-07-23 20:13:07 +08:00
|
|
|
self.ensure_no_info_bar(timeout=3)
|
2020-03-29 01:22:46 +08:00
|
|
|
|
2020-11-24 17:45:00 +08:00
|
|
|
def _opponent_fleet_check_all(self):
|
2021-09-23 02:31:08 +08:00
|
|
|
if self.config.Exercise_OpponentChooseMode != 'leftmost':
|
2020-11-24 17:45:00 +08:00
|
|
|
super()._opponent_fleet_check_all()
|
|
|
|
|
2021-09-23 02:31:08 +08:00
|
|
|
def _opponent_sort(self, method=None):
|
|
|
|
if method is None:
|
|
|
|
method = self.config.Exercise_OpponentChooseMode
|
|
|
|
if method != 'leftmost':
|
|
|
|
return super()._opponent_sort(method=method)
|
2020-11-24 17:45:00 +08:00
|
|
|
else:
|
2021-09-23 02:31:08 +08:00
|
|
|
return [0, 1, 2, 3]
|
2020-11-24 17:45:00 +08:00
|
|
|
|
2020-03-29 01:22:46 +08:00
|
|
|
def _exercise_once(self):
|
2020-07-20 20:37:54 +08:00
|
|
|
"""Execute exercise once.
|
|
|
|
|
|
|
|
This method handles exercise refresh and exercise failure.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: True if success to defeat one opponent. False if failed to defeat any opponent and refresh exhausted.
|
|
|
|
"""
|
2020-07-20 07:42:06 +08:00
|
|
|
self._opponent_fleet_check_all()
|
2020-07-20 20:37:54 +08:00
|
|
|
while 1:
|
|
|
|
for opponent in self._opponent_sort():
|
2021-09-23 02:31:08 +08:00
|
|
|
logger.hr(f'Opponent {opponent}', level=2)
|
2020-07-20 20:37:54 +08:00
|
|
|
success = self._combat(opponent)
|
|
|
|
if success:
|
|
|
|
return success
|
|
|
|
|
|
|
|
if self.opponent_change_count >= 5:
|
|
|
|
return False
|
|
|
|
|
2020-03-29 01:22:46 +08:00
|
|
|
self._new_opponent()
|
2020-07-20 20:37:54 +08:00
|
|
|
self._opponent_fleet_check_all()
|
2020-03-29 01:22:46 +08:00
|
|
|
|
2020-07-20 07:42:06 +08:00
|
|
|
def _exercise_easiest_else_exp(self):
|
2020-07-20 20:37:54 +08:00
|
|
|
"""Try easiest first, if unable to beat easiest opponent then switch to max exp opponent and accept the loss.
|
|
|
|
|
|
|
|
This method handles exercise refresh and exercise failure.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: True if success to defeat one opponent. False if failed to defeat any opponent and refresh exhausted.
|
|
|
|
"""
|
2021-09-23 02:31:08 +08:00
|
|
|
method = "easiest_else_exp"
|
|
|
|
restore = self.config.Exercise_LowHpThreshold
|
|
|
|
threshold = self.config.Exercise_LowHpThreshold
|
2020-07-20 07:42:06 +08:00
|
|
|
self._opponent_fleet_check_all()
|
|
|
|
while 1:
|
2021-09-23 02:31:08 +08:00
|
|
|
opponents = self._opponent_sort(method=method)
|
|
|
|
logger.hr(f'Opponent {opponents[0]}', level=2)
|
|
|
|
self.config.override(Exercise_LowHpThreshold=threshold)
|
2020-07-20 07:42:06 +08:00
|
|
|
success = self._combat(opponents[0])
|
|
|
|
if success:
|
2021-09-23 02:31:08 +08:00
|
|
|
self.config.override(Exercise_LowHpThreshold=restore)
|
2020-07-20 07:42:06 +08:00
|
|
|
return success
|
|
|
|
else:
|
2020-07-20 20:37:54 +08:00
|
|
|
if self.opponent_change_count < 5:
|
|
|
|
logger.info("Cannot beat calculated easiest opponent, refresh")
|
2020-07-20 07:42:06 +08:00
|
|
|
self._new_opponent()
|
2020-07-20 20:37:54 +08:00
|
|
|
self._opponent_fleet_check_all()
|
|
|
|
continue
|
2020-07-20 07:42:06 +08:00
|
|
|
else:
|
2020-07-20 20:37:54 +08:00
|
|
|
logger.info("Cannot beat calculated easiest opponent, MAX EXP then")
|
2021-09-23 02:31:08 +08:00
|
|
|
method = "max_exp"
|
|
|
|
threshold = 0
|
2020-07-20 07:42:06 +08:00
|
|
|
|
2020-07-20 20:37:54 +08:00
|
|
|
def _get_opponent_change_count(self):
|
|
|
|
"""
|
|
|
|
Same day, count set to last known change count or 6 i.e. no refresh
|
|
|
|
New day, count set to 0 i.e. can change up to 5 times
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
int:
|
|
|
|
"""
|
2021-09-25 23:29:10 +08:00
|
|
|
record = self.config.Exercise_OpponentRefreshRecord
|
2021-09-23 22:04:03 +08:00
|
|
|
update = get_server_last_update('00:00')
|
2020-07-20 20:37:54 +08:00
|
|
|
if record.date() == update.date():
|
|
|
|
# Same Day
|
2021-09-25 23:29:10 +08:00
|
|
|
return self.config.Exercise_OpponentRefreshValue
|
2020-07-20 20:37:54 +08:00
|
|
|
else:
|
|
|
|
# New Day
|
2021-09-25 23:29:10 +08:00
|
|
|
self.config.set_record(Exercise_OpponentRefreshValue=0)
|
2020-07-20 20:37:54 +08:00
|
|
|
return 0
|
2020-07-20 07:42:06 +08:00
|
|
|
|
2023-04-05 12:30:06 +08:00
|
|
|
def server_support_ocr_reset_remain(self) -> bool:
|
2023-04-09 12:52:35 +08:00
|
|
|
return self.config.SERVER in ['cn', 'en', 'jp']
|
2023-04-05 12:30:06 +08:00
|
|
|
|
2023-03-30 22:03:02 +08:00
|
|
|
def _get_exercise_reset_remain(self):
|
|
|
|
"""
|
|
|
|
Returns:
|
2023-04-03 16:34:56 +08:00
|
|
|
datetime.timedelta
|
2023-03-30 22:03:02 +08:00
|
|
|
"""
|
2023-04-05 12:30:06 +08:00
|
|
|
result = OCR_PERIOD_REMAIN.ocr(self.device.image)
|
|
|
|
return result
|
2023-03-30 22:03:02 +08:00
|
|
|
|
2023-04-09 12:52:35 +08:00
|
|
|
def _get_exercise_strategy(self):
|
|
|
|
"""
|
|
|
|
Returns:
|
|
|
|
int: ExercisePreserve, X times to remain
|
|
|
|
list, int: Admiral trial time period
|
|
|
|
"""
|
|
|
|
if self.config.Exercise_ExerciseStrategy == "aggressive":
|
|
|
|
preserve = 0
|
|
|
|
admiral_interval = None
|
|
|
|
else:
|
|
|
|
preserve = 5
|
|
|
|
admiral_interval = ADMIRAL_TRIAL_HOUR_INTERVAL[self.config.Exercise_ExerciseStrategy]
|
2023-04-11 22:08:38 +08:00
|
|
|
|
2023-04-09 12:52:35 +08:00
|
|
|
return preserve, admiral_interval
|
|
|
|
|
2020-07-20 20:37:54 +08:00
|
|
|
def run(self):
|
2020-03-29 01:22:46 +08:00
|
|
|
self.ui_ensure(page_exercise)
|
|
|
|
|
2020-07-20 20:37:54 +08:00
|
|
|
self.opponent_change_count = self._get_opponent_change_count()
|
|
|
|
logger.attr("Change_opponent_count", self.opponent_change_count)
|
2023-04-11 22:08:38 +08:00
|
|
|
logger.attr('Exercise_ExerciseStrategy', self.config.Exercise_ExerciseStrategy)
|
2023-04-09 12:52:35 +08:00
|
|
|
self.preserve, admiral_interval = self._get_exercise_strategy()
|
2023-03-30 20:47:38 +08:00
|
|
|
|
2023-04-05 12:30:06 +08:00
|
|
|
if not self.server_support_ocr_reset_remain():
|
|
|
|
logger.info(f'Server {self.config.SERVER} does not yet support OCR exercise reset remain time')
|
|
|
|
logger.info('Please contact the developer to improve as soon as possible')
|
|
|
|
remain_time = timedelta(days=0)
|
|
|
|
else:
|
|
|
|
remain_time = OCR_PERIOD_REMAIN.ocr(self.device.image)
|
2023-04-11 22:08:38 +08:00
|
|
|
logger.info(f'Exercise period remain: {remain_time}')
|
|
|
|
|
2023-04-09 12:52:35 +08:00
|
|
|
if admiral_interval is not None and remain_time:
|
|
|
|
admiral_start, admiral_end = admiral_interval
|
2023-04-11 22:08:38 +08:00
|
|
|
|
|
|
|
if admiral_start > int(remain_time.total_seconds() // 3600) >= admiral_end: # set time for getting admiral
|
2023-04-09 14:09:35 +08:00
|
|
|
logger.info('Reach set time for admiral trial, using all attempts.')
|
2023-04-11 22:08:38 +08:00
|
|
|
self.preserve = 0
|
|
|
|
elif int(remain_time.total_seconds() // 3600) < 6: # if not set to "sun18", still depleting at sunday 18pm.
|
2023-04-09 14:09:35 +08:00
|
|
|
logger.info('Exercise period remain less than 6 hours, using all attempts.')
|
2023-04-09 12:52:35 +08:00
|
|
|
self.preserve = 0
|
2023-04-11 22:08:38 +08:00
|
|
|
else:
|
|
|
|
logger.info(f'Preserve {self.preserve} exercise')
|
2023-03-30 20:47:38 +08:00
|
|
|
|
2020-03-29 01:22:46 +08:00
|
|
|
while 1:
|
2020-07-20 07:42:06 +08:00
|
|
|
self.remain = OCR_EXERCISE_REMAIN.ocr(self.device.image)
|
2023-04-09 12:52:35 +08:00
|
|
|
if self.remain <= self.preserve:
|
2020-03-29 01:22:46 +08:00
|
|
|
break
|
|
|
|
|
2021-09-23 02:31:08 +08:00
|
|
|
logger.hr(f'Exercise remain {self.remain}', level=1)
|
|
|
|
if self.config.Exercise_OpponentChooseMode == "easiest_else_exp":
|
2020-07-20 07:42:06 +08:00
|
|
|
success = self._exercise_easiest_else_exp()
|
|
|
|
else:
|
|
|
|
success = self._exercise_once()
|
2020-03-29 01:22:46 +08:00
|
|
|
if not success:
|
|
|
|
logger.info('New opponent exhausted')
|
|
|
|
break
|
2023-04-09 12:52:35 +08:00
|
|
|
|
2021-09-23 02:31:08 +08:00
|
|
|
# self.equipment_take_off_when_finished()
|
2020-03-29 01:22:46 +08:00
|
|
|
|
2021-09-23 02:31:08 +08:00
|
|
|
# Scheduler
|
2021-09-26 18:26:58 +08:00
|
|
|
with self.config.multi_set():
|
|
|
|
self.config.set_record(Exercise_OpponentRefreshValue=self.opponent_change_count)
|
2023-04-09 12:52:35 +08:00
|
|
|
if self.remain <= self.preserve or self.opponent_change_count >= 5:
|
2021-09-26 18:26:58 +08:00
|
|
|
self.config.task_delay(server_update=True)
|
|
|
|
else:
|
|
|
|
self.config.task_delay(success=False)
|