Skip to content

Instantly share code, notes, and snippets.

@luckytyphlosion
Created February 13, 2022 16:58
Show Gist options
  • Save luckytyphlosion/0dc830a6e8fc6eb6744ef07b3170f732 to your computer and use it in GitHub Desktop.
Save luckytyphlosion/0dc830a6e8fc6eb6744ef07b3170f732 to your computer and use it in GitHub Desktop.
Simulates time to get DarkDrill + Areagrab for BN5 All Icons Omega Navis strat.
# =============================================================================
# Copyright(c) 2022 luckytyphlosion
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# =============================================================================
import random
import collections
PLUS_CHIP = 1
FULLCUST = 2
DARKDRILL = 3
OTHER_CHIP = 4
AREAGRAB_A = 5
AREAGRAB_L = 6
AREAGRAB_S = 7
METAGEL_F = 8
METAGEL_N = 9
METAGEL_T = 10
def is_areagrab(chip):
return chip >= AREAGRAB_A
def cust_has_areagrab(cust):
return any(chip >= AREAGRAB_A for chip in cust)
def get_areagrab_chip_tally(cust):
areagrab_tally = collections.defaultdict(int)
for chip in cust:
if chip >= AREAGRAB_A:
areagrab_tally[chip] += 1
sorted_areagrab_tally = sorted(areagrab_tally.items(), key=lambda x: x[1], reverse=True)
return sorted_areagrab_tally
def debug_print(s):
if False:
print(s)
class Folder:
__slots__ = ("unshuffled_chips", "_cust_size", "chips", "soul_time")
def __init__(self, unshuffled_chips, _cust_size):
self.unshuffled_chips = unshuffled_chips
self._cust_size = _cust_size
self.soul_time = 0
def shuffle_chips(self):
self.chips = list(self.unshuffled_chips)
chips = self.chips
for i in range(29):
a_index = random.randrange(29)
b_index = random.randrange(29)
a = chips[a_index]
b = chips[b_index]
chips[a_index] = b
chips[b_index] = a
#debug_print(f"self.chips: {self.chips}")
def get_cust_size(self, turn_1=False):
if turn_1:
return self.cust_size - 1
else:
return self.cust_size
def get_cust_chips(self, turn_1=False):
if turn_1:
return self.chips[:self.cust_size - 1]
else:
return self.chips[:self.cust_size]
@property
def cust_size(self):
if self.soul_time > 0:
return 10
else:
return self._cust_size
@staticmethod
def turn_1_to_number(turn_1):
return 1 if turn_1 else 0
def greedy_pick_chips(self, turn_1=False, darkdrill_replace=False):
turn_1_num = Folder.turn_1_to_number(turn_1)
cust = self.get_cust_chips(turn_1)
new_cust = []
num_chips_picked = 0
areagrab_chip_tally = get_areagrab_chip_tally(cust)
if len(areagrab_chip_tally) > 0:
if len(areagrab_chip_tally) == 1:
keep_one_areagrab = 1
else:
keep_one_areagrab = 0
areagrab_chip = areagrab_chip_tally[0][0]
num_areagrab_chip = areagrab_chip_tally[0][1]
for i in range(num_areagrab_chip - keep_one_areagrab):
cust.remove(areagrab_chip)
num_chips_picked += num_areagrab_chip - keep_one_areagrab
for chip in cust:
if num_chips_picked < 5 - turn_1_num and chip in (FULLCUST, OTHER_CHIP, PLUS_CHIP):
num_chips_picked += 1
else:
new_cust.append(chip)
#debug_print(f"cust: {cust}, new_cust: {new_cust}")
if darkdrill_replace:
rest_of_chips_slice = self.chips[self.cust_size - turn_1_num:]
rest_of_chips_slice.remove(DARKDRILL)
self.chips = new_cust + [DARKDRILL] + rest_of_chips_slice
else:
self.chips = new_cust + self.chips[self.cust_size - turn_1_num:]
debug_print(f"self.chips: {self.chips}")
self.soul_time -= 1
EMOTION_NORMAL = 0
EMOTION_WORRIED = 1
EMOTION_ANGRY = 2
EMOTION_FULLSYNC = 3
NON_WORRIED_EMOTIONS = (EMOTION_NORMAL, EMOTION_ANGRY, EMOTION_FULLSYNC)
# 7 normal, 7 worried, 1 angry, 1 fullsync
class EmotionWeights:
__slots__ = ("new_emotions", "new_emotion_weights")
def __init__(self, new_emotions, new_emotion_weights):
self.new_emotions = new_emotions
self.new_emotion_weights = new_emotion_weights
emotion_to_new_emotions_and_weights = {
EMOTION_NORMAL: EmotionWeights(
(EMOTION_WORRIED, EMOTION_ANGRY, EMOTION_FULLSYNC),
(7, 1, 1)
),
EMOTION_WORRIED: EmotionWeights(
(EMOTION_NORMAL, EMOTION_ANGRY, EMOTION_FULLSYNC),
(7, 1, 1)
),
EMOTION_ANGRY: EmotionWeights(
(EMOTION_NORMAL, EMOTION_WORRIED, EMOTION_FULLSYNC),
(7, 7, 1)
),
EMOTION_FULLSYNC: EmotionWeights(
(EMOTION_NORMAL, EMOTION_WORRIED, EMOTION_ANGRY),
(7, 7, 1)
),
}
EMOTION_REACTION_TIME = 10
CHIP_SELECTION_TIME = 240
PRESS_OK_TO_TURN_START_TIME = 129
OPEN_CUST_SCREEN_TIME = 18
FSTGAUGE_TIME = 170
class EmotionBug:
__slots__ = ("cur_emotion", "emotion_timer")
def __init__(self):
self.cur_emotion = EMOTION_NORMAL
self.emotion_timer = 0
def cycle_new_emotion(self):
new_emotions_and_weights = emotion_to_new_emotions_and_weights[self.cur_emotion]
self.cur_emotion = random.choices(new_emotions_and_weights.new_emotions, weights=new_emotions_and_weights.new_emotion_weights)[0]
return self.cur_emotion
def cycle_emotions_for_duration(self, duration):
while True:
if duration + self.emotion_timer < 60:
self.emotion_timer += duration
break
else:
duration -= 60 - self.emotion_timer
self.emotion_timer = 0
self.cycle_new_emotion()
def wait_emotion(self, emotion, gauge_threshold=0):
time_waited = 0
time_waited += 60 - self.emotion_timer
cur_gauge = 60 - self.emotion_timer
self.emotion_timer = 0
if type(emotion) == int:
emotion = (emotion,)
while True:
self.cycle_new_emotion()
if self.cur_emotion in emotion:
if cur_gauge >= gauge_threshold:
break
elif cur_gauge < gauge_threshold and gauge_threshold - cur_gauge < 60 and self.emotion_timer + gauge_threshold - cur_gauge + EMOTION_REACTION_TIME < 60:
time_waited += gauge_threshold - cur_gauge
self.emotion_timer += gauge_threshold - cur_gauge
break
else:
time_waited += 60
cur_gauge += 60
time_waited += EMOTION_REACTION_TIME
self.emotion_timer += EMOTION_REACTION_TIME
return time_waited
def is_normal(self):
return self.cur_emotion == EMOTION_NORMAL
def is_worried(self):
return self.cur_emotion == EMOTION_WORRIED
BRANCH_BEST_DRAW = 0
BRANCH_AREAGRAB_ONLY = 1
BRANCH_DARKDRILL_ONLY = 2
BRANCH_NONE = 3
# knightman alpha
# shadowman alpha
# tomahawkman alpha
# numberman alpha
# colonel beta
# knightman beta
# shadowman beta
# tomahawkman beta
# numberman beta
# colonel omega (x2?)
# knightman omega
# shadowman omega
# tomahawkman omega
# numberman omega
# blizman beta
# shademan beta
# cloudman beta
# cosmoman beta
# cosmoman omega
# colonel DS x2
# shadowman DS x2
# tomahawkman DS x2
# numberman DS x2
# bass beta
# bass omega
def main():
#random.seed(102)
NUM_TRIALS = 10000
CUST_SIZE = 8
AREAGRABS = [AREAGRAB_S, AREAGRAB_S, METAGEL_T, AREAGRAB_S]
NUM_PLUS_CHIPS = 10
unshuffled_chips = [FULLCUST] + [DARKDRILL] + AREAGRABS + [PLUS_CHIP] * NUM_PLUS_CHIPS
unshuffled_chips = unshuffled_chips + [OTHER_CHIP] * (29 - len(unshuffled_chips))
print(f"len(unshuffled_chips): {len(unshuffled_chips)}")
total_frames_before_ready_to_darkdrill = 0
branches = collections.defaultdict(int)
for cur_trial in range(NUM_TRIALS):
if cur_trial % 100 == 0:
print(f"cur_trial: {cur_trial}")
folder = Folder(unshuffled_chips, CUST_SIZE)
emotion_bug = EmotionBug()
folder.shuffle_chips()
cust = folder.get_cust_chips(turn_1=True)
if DARKDRILL in cust and cust_has_areagrab(cust):
branches[BRANCH_BEST_DRAW] += 1
total_frames_before_ready_to_darkdrill += CHIP_SELECTION_TIME + PRESS_OK_TO_TURN_START_TIME
elif cust_has_areagrab(cust):
branches[BRANCH_AREAGRAB_ONLY] += 1
folder.greedy_pick_chips(turn_1=True, darkdrill_replace=True)
total_frames_before_ready_to_darkdrill += CHIP_SELECTION_TIME + PRESS_OK_TO_TURN_START_TIME + FSTGAUGE_TIME
emotion_bug.cycle_emotions_for_duration(FSTGAUGE_TIME)
if FULLCUST in cust:
turn_1_fight_frames = emotion_bug.wait_emotion(EMOTION_WORRIED)
else:
turn_1_fight_frames = emotion_bug.wait_emotion(EMOTION_WORRIED, 256)
#print(f"turn_1_fight_frames: {turn_1_fight_frames}")
total_frames_before_ready_to_darkdrill += turn_1_fight_frames
total_frames_before_ready_to_darkdrill += OPEN_CUST_SCREEN_TIME + CHIP_SELECTION_TIME + PRESS_OK_TO_TURN_START_TIME
cust = folder.get_cust_chips()
if FULLCUST in cust:
turn_2_fight_frames = emotion_bug.wait_emotion(NON_WORRIED_EMOTIONS)
else:
turn_2_fight_frames = emotion_bug.wait_emotion(NON_WORRIED_EMOTIONS, 256)
#print(f"turn_2_fight_frames: {turn_2_fight_frames}")
total_frames_before_ready_to_darkdrill += turn_2_fight_frames
total_frames_before_ready_to_darkdrill += OPEN_CUST_SCREEN_TIME + CHIP_SELECTION_TIME + PRESS_OK_TO_TURN_START_TIME
elif DARKDRILL in cust:
branches[BRANCH_DARKDRILL_ONLY] += 1
numbersoul_used = False
total_frames_before_ready_to_darkdrill += FSTGAUGE_TIME
turn_1 = True
while True:
total_frames_before_ready_to_darkdrill += CHIP_SELECTION_TIME + PRESS_OK_TO_TURN_START_TIME
if cust_has_areagrab(cust) and DARKDRILL in cust:
break
if FULLCUST not in cust:
total_frames_before_ready_to_darkdrill += 256
total_frames_before_ready_to_darkdrill += OPEN_CUST_SCREEN_TIME
folder.greedy_pick_chips(turn_1=turn_1)
if not numbersoul_used and PLUS_CHIP in cust:
numbersoul_used = True
folder.soul_time = 3
turn_1 = False
cust = folder.get_cust_chips()
else:
branches[BRANCH_NONE] += 1
folder.greedy_pick_chips(turn_1=True, darkdrill_replace=True)
total_frames_before_ready_to_darkdrill += CHIP_SELECTION_TIME + PRESS_OK_TO_TURN_START_TIME + FSTGAUGE_TIME
emotion_bug.cycle_emotions_for_duration(FSTGAUGE_TIME)
if FULLCUST in cust:
turn_1_fight_frames = emotion_bug.wait_emotion(EMOTION_WORRIED)
else:
turn_1_fight_frames = emotion_bug.wait_emotion(EMOTION_WORRIED, 256)
#print(f"turn_1_fight_frames: {turn_1_fight_frames}")
total_frames_before_ready_to_darkdrill += turn_1_fight_frames
total_frames_before_ready_to_darkdrill += OPEN_CUST_SCREEN_TIME + CHIP_SELECTION_TIME + PRESS_OK_TO_TURN_START_TIME
cust = folder.get_cust_chips()
if FULLCUST in cust:
turn_2_fight_frames = emotion_bug.wait_emotion(NON_WORRIED_EMOTIONS)
else:
turn_2_fight_frames = emotion_bug.wait_emotion(NON_WORRIED_EMOTIONS, 256)
folder.greedy_pick_chips()
#print(f"turn_2_fight_frames: {turn_2_fight_frames}")
total_frames_before_ready_to_darkdrill += turn_2_fight_frames
total_frames_before_ready_to_darkdrill += OPEN_CUST_SCREEN_TIME + CHIP_SELECTION_TIME + PRESS_OK_TO_TURN_START_TIME
while True:
cust = folder.get_cust_chips()
if DARKDRILL in cust and cust_has_areagrab(cust):
break
if FULLCUST not in cust:
total_frames_before_ready_to_darkdrill += 256
total_frames_before_ready_to_darkdrill += OPEN_CUST_SCREEN_TIME + CHIP_SELECTION_TIME + PRESS_OK_TO_TURN_START_TIME
folder.greedy_pick_chips()
#else:
#
##print(chips)
#if .count(OBJECT_CHIP) >= 1:
# object_chip_occurrences += 1
avg_frames_before_ready_to_darkdrill = total_frames_before_ready_to_darkdrill/NUM_TRIALS
output = ""
output += f"avg_frames_before_ready_to_darkdrill: {avg_frames_before_ready_to_darkdrill}\n"
output += "".join(f"{branch}: {num_occurrences}\n" for branch, num_occurrences in branches.items())
print(output)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment