def step(self, gamestate, smashbot_state, opponent_state): self._propagate = (gamestate, smashbot_state, opponent_state) #If we can't interrupt the chain, just continue it if self.chain != None and not self.chain.interruptible: self.chain.step(gamestate, smashbot_state, opponent_state) return framesleft = Punish.framesleft(opponent_state, self.framedata, smashbot_state) # This is off by one for hitstun framesleft -= 1 shinerange = 9.9 if smashbot_state.action == Action.RUNNING: shinerange = 12.8 if smashbot_state.action == Action.DASHING: shinerange = 9.5 # If we shine too close to the edge while accelerating horizontally, we can slide offstage and get into trouble distance_from_edge = melee.stages.EDGE_GROUND_POSITION[gamestate.stage] - abs(smashbot_state.position.x) edgetooclose = smashbot_state.action == Action.EDGE_TEETERING_START or distance_from_edge < 5 # Try to do the shine if gamestate.distance < shinerange and not edgetooclose: # Emergency backup shine. If we don't shine now, they'll get out of the combo if framesleft == 1: self.chain = None self.pickchain(Chains.Waveshine) return # Cut the run short and just shine now. Don't wait for the cross-up # This is here to prevent running too close to the edge and sliding off if smashbot_state.action in [Action.RUNNING, Action.RUN_BRAKE, Action.CROUCH_START] and distance_from_edge < 16: self.chain = None self.pickchain(Chains.Waveshine) return # We always want to try to shine our opponent towards the center of the stage # If we are lined up right now, do the shine if (smashbot_state.position.x < opponent_state.position.x < 0) or (0 < opponent_state.position.x < smashbot_state.position.x): self.chain = None self.pickchain(Chains.Waveshine) return # If we are running away from our opponent, just shine now onright = opponent_state.position.x < smashbot_state.position.x if (smashbot_state.speed_ground_x_self > 0) == onright: self.chain = None self.pickchain(Chains.Waveshine) return if smashbot_state.action == Action.LANDING_SPECIAL and smashbot_state.action_frame < 28: self.pickchain(Chains.Nothing) return if not (smashbot_state.action == Action.DOWN_B_GROUND_START and smashbot_state.action_frame in [1,2]): self.pickchain(Chains.Run, [opponent_state.position.x > smashbot_state.position.x]) return return
def shouldapproach(smashbot_state, opponent_state, gamestate, framedata, logger): if len(gamestate.projectiles) > 0: return False # Specify that this needs to be platform approach framesleft = Punish.framesleft(opponent_state, framedata, smashbot_state) if logger: logger.log("Notes", " framesleft: " + str(framesleft) + " ", concat=True) if framesleft >= 9: return True return False
def caninfinite(smashbot_state, opponent_state, gamestate, framedata, difficulty): isroll = framedata.is_roll(opponent_state.character, opponent_state.action) if opponent_state.action in [Action.SHIELD_START, Action.SHIELD, \ Action.SHIELD_STUN, Action.SHIELD_REFLECT]: return False # Don't try to infinite if we're on a platform if smashbot_state.position.y > 2 or opponent_state.position.y > 2: return False # Should we try a waveshine infinite? # They need to have high friction and not fall down if opponent_state.action in [Action.STANDING, Action.TURNING, Action.DASHING, Action.RUNNING, \ Action.WALK_SLOW, Action.WALK_MIDDLE, Action.WALK_FAST]: return False framesleft = Punish.framesleft(opponent_state, framedata, smashbot_state) # This is off by one for hitstun framesleft -= 1 # Give up the infinite if we're in our last dashing frame, and are getting close to the edge # We are at risk of running off the edge when this happens if (smashbot_state.action == Action.DASHING and smashbot_state.action_frame >= 11): if (smashbot_state.speed_ground_x_self > 0) == (smashbot_state.position.x > 0): edge_x = melee.stages.EDGE_GROUND_POSITION[gamestate.stage] if opponent_state.position.x < 0: edge_x = -edge_x edgedistance = abs(edge_x - smashbot_state.position.x) if edgedistance < 16: return False # If opponent is attacking, don't infinite if framedata.is_attack(opponent_state.character, opponent_state.action): return False # If opponent is going to slide to the edge, then we have to stop endposition = opponent_state.position.x + framedata.slide_distance( opponent_state, opponent_state.speed_x_attack, framesleft) if abs(endposition) + 5 > melee.stages.EDGE_GROUND_POSITION[ gamestate.stage]: return False if framedata.characterdata[opponent_state.character]["Friction"] >= 0.06 and \ opponent_state.hitstun_frames_left > 1 and not isroll and opponent_state.on_ground \ and opponent_state.percent < Infinite.killpercent(opponent_state): return True return False
def step(self, gamestate, smashbot_state, opponent_state): self._propagate = (gamestate, smashbot_state, opponent_state) # TODO Should this only be set once per instance? self.movingright = opponent_state.speed_x_attack + opponent_state.speed_ground_x_self > 0 #If we can't interrupt the chain, just continue it if self.chain != None and not self.chain.interruptible: self.chain.step(gamestate, smashbot_state, opponent_state) return framesleft = Punish.framesleft(opponent_state, self.framedata) # This is off by one for hitstun framesleft -= 1 shinerange = 11.8 if smashbot_state.action == Action.DASHING: shinerange = 9 # Try to do the shine if gamestate.distance < shinerange: # emergency backup shine if framesleft == 1: self.chain = None self.pickchain(Chains.Waveshine) return onright = opponent_state.x < smashbot_state.x opponentspeed = opponent_state.speed_x_attack + opponent_state.speed_ground_x_self # If opponent isn't moving, then just try to shine back towards the middle if abs(opponentspeed) > 0.01: self.movingright = opponentspeed > 0 # We always want to try to shine our opponent towards the center of the stage # If we are lined up right now, do the shine if smashbot_state.x < opponent_state.x < 0 or \ 0 < opponent_state.x < smashbot_state.x: self.chain = None self.pickchain(Chains.Waveshine) return # If we are running away from our opponent, just shine now if (smashbot_state.speed_ground_x_self > 0) == onright: self.chain = None self.pickchain(Chains.Waveshine) return if smashbot_state.action == Action.LANDING_SPECIAL and smashbot_state.action_frame < 28: self.pickchain(Chains.Nothing) return self.pickchain(Chains.Run, [opponent_state.speed_x_attack > 0]) return
def step(self, gamestate, smashbot_state, opponent_state): self._propagate = (gamestate, smashbot_state, opponent_state) # -1 means auto-adjust difficulty based on stocks remaining if self.set_difficulty == -1: self.difficulty = smashbot_state.stock else: self.difficulty = self.set_difficulty if SelfDestruct.shouldsd(gamestate, smashbot_state, opponent_state): self.picktactic(Tactics.SelfDestruct) return # Reset the approach state after 1 second # Or if opponent becomes invulnerable if self.approach and ((abs(self.approach_frame - gamestate.frame) > 60) or (opponent_state.invulnerability_left > 0)): self.approach_frame = -123 self.approach = False # Randomly approach sometimes rather than keeping distance # Should happen on average once per 2 seconds # The effect will last for about 1 second # On the first two difficulties, just always approach if (random.randint(0, 120) == 0 or (self.difficulty >= 4 and opponent_state.action != Action.CROUCHING )) and (opponent_state.invulnerability_left == 0): self.approach = True self.approach_frame = gamestate.frame if self.logger: self.logger.log("Notes", " approach: " + str(self.approach) + " ", concat=True) if Mitigate.needsmitigation(smashbot_state): self.picktactic(Tactics.Mitigate) return if self.tactic and not self.tactic.isinteruptible(): self.tactic.step(gamestate, smashbot_state, opponent_state) return # If we're stuck in a lag state, just do nothing. Trying an action might just # buffer an input we don't want if Wait.shouldwait(gamestate, smashbot_state, opponent_state, self.framedata): self.picktactic(Tactics.Wait) return if Recover.needsrecovery(smashbot_state, opponent_state, gamestate): self.picktactic(Tactics.Recover) return if Celebrate.deservescelebration(smashbot_state, opponent_state): self.picktactic(Tactics.Celebrate) return # Difficulty 5 is a debug / training mode # Don't do any attacks, and don't do any shielding # Take attacks, DI, and recover if self.difficulty == 5: self.picktactic(Tactics.KeepDistance) return if Defend.needsprojectiledefense(smashbot_state, opponent_state, gamestate, self.logger): self.picktactic(Tactics.Defend) return # If we can infinite our opponent, do that! if Infinite.caninfinite(smashbot_state, opponent_state, gamestate, self.framedata, self.difficulty): self.picktactic(Tactics.Infinite) return # If we can juggle opponent in the air, do that if Juggle.canjuggle(smashbot_state, opponent_state, gamestate, self.framedata, self.difficulty): self.picktactic(Tactics.Juggle) return # If we can punish our opponent for a laggy move, let's do that if Punish.canpunish(smashbot_state, opponent_state, gamestate, self.framedata): self.picktactic(Tactics.Punish) return # Do we need to defend an attack? if Defend.needsdefense(smashbot_state, opponent_state, gamestate, self.framedata): self.picktactic(Tactics.Defend) return # Can we edge guard them? if Edgeguard.canedgeguard(smashbot_state, opponent_state, gamestate): self.picktactic(Tactics.Edgeguard) return # Can we shield pressure them? if Pressure.canpressure(opponent_state, gamestate): self.picktactic(Tactics.Pressure) return if Retreat.shouldretreat(smashbot_state, opponent_state, gamestate, not self.approach): self.picktactic(Tactics.Retreat) return if Challenge.canchallenge(smashbot_state, opponent_state, gamestate, self.framedata, self.difficulty): self.picktactic(Tactics.Challenge) return if Approach.shouldapproach(smashbot_state, opponent_state, gamestate, self.framedata, self.logger) or \ (self.approach and not Approach.approach_too_dangerous(smashbot_state, opponent_state, gamestate, self.framedata)): self.picktactic(Tactics.Approach) return self.picktactic(Tactics.KeepDistance)
def step(self): opponent_state = self.opponent_state smashbot_state = self.smashbot_state recoverhigh = self.canrecoverhigh() #If we can't interrupt the chain, just continue it if self.chain != None and not self.chain.interruptible: self.chain.step() return if Dropdownshine.inrange(self.smashbot_state, self.opponent_state, self.framedata): self.pickchain(Chains.Dropdownshine) return if smashbot_state.action == Action.EDGE_CATCHING: self.pickchain(Chains.Nothing) return # How many frames will it take to get to our opponent right now? onedge = smashbot_state.action in [ Action.EDGE_HANGING, Action.EDGE_CATCHING ] opponentonedge = opponent_state.action in [ Action.EDGE_HANGING, Action.EDGE_CATCHING ] # Stand up if opponent attacks us proj_incoming = Defend.needsprojectiledefense( self.smashbot_state, self.opponent_state, self.gamestate) and smashbot_state.invulnerability_left <= 2 samusgrapple = opponent_state.character == Character.SAMUS and opponent_state.action == Action.SWORD_DANCE_4_LOW and \ -25 < opponent_state.y < 0 and smashbot_state.invulnerability_left <= 2 hitframe = self.framedata.inrange(opponent_state, smashbot_state, self.gamestate.stage) framesleft = hitframe - opponent_state.action_frame if proj_incoming or samusgrapple or ( hitframe != 0 and onedge and framesleft < 5 and smashbot_state.invulnerability_left < 2): # Unless the attack is a grab, then don't bother if not self.framedata.isgrab(opponent_state.character, opponent_state.action): if self.isupb(): #TODO: Make this a chain self.chain = None self.controller.press_button(Button.BUTTON_L) return else: self.chain = None self.pickchain(Chains.DI, [0.5, 0.65]) return # Special exception for Fox/Falco illusion # Since it is dumb and technically a projectile if opponent_state.character in [Character.FOX, Character.FALCO]: if opponent_state.action in [Action.SWORD_DANCE_2_MID]: self.chain = None self.pickchain(Chains.DI, [0.5, 0.65]) return # What recovery options does opponent have? landonstage = False grabedge = False # they have to commit to an up-b to recover mustupb = False canrecover = True djheight = self.framedata.getdjheight(opponent_state) edgegrabframes = self.snaptoedgeframes() # How heigh can they go with a jump? potentialheight = djheight + opponent_state.y if potentialheight < -23: mustupb = True # Now consider UP-B # Have they already UP-B'd? if self.isupb(): if self.upbstart == 0: self.upbstart = opponent_state.y # If they are halfway through the up-b, then subtract out what they've alrady used potentialheight = self.upbheight() + self.upbstart elif opponent_state.action == Action.DEAD_FALL: potentialheight = opponent_state.y else: potentialheight += self.upbheight() # Cpt Falcon's up-b causes him to distort his model by a crazy amount. Giving him # the ability to get on the stage easier. Adjust for this adjustedheight = potentialheight if opponent_state.character == Character.CPTFALCON and self.isupb(): adjustedheight += 12 # Adjust upwards a little to have some wiggle room if adjustedheight > -5: landonstage = True if potentialheight > -23: grabedge = True if potentialheight < -30: mustupb = True canrecover = False # Split the logic into two: # A) We are on the edge # B) We are on the stage if smashbot_state.action in [ Action.EDGE_HANGING, Action.EDGE_CATCHING ]: # If opponent can't recover, then just get onto the stage! if not canrecover: #TODO: Make this a chain self.chain = None self.controller.press_button(Button.BUTTON_L) return # Don't roll up too early for Falcon falconupearly = opponent_state.character == Character.CPTFALCON and \ opponent_state.action == Action.SWORD_DANCE_3_LOW and opponent_state.action_frame <= 12 # Roll up to edgehog if self.isupb() and not landonstage and not falconupearly: #TODO: Make this a chain self.chain = None self.controller.press_button(Button.BUTTON_L) return # Edgestall # For Fox and Falco, we have a different edgestall strategy. Only edgestall if they start a FireFox if opponent_state.character in [Character.FOX, Character.FALCO]: # Are they in the start of a firefox? # But make sure they can't grab the edge in the middle of it edgedistance = abs(opponent_state.x) - ( melee.stages.edgegroundposition(self.gamestate.stage) + 15) inrange = (-5 > opponent_state.y > -23) and (edgedistance < 15) if opponent_state.action == Action.SWORD_DANCE_3_LOW and opponent_state.action_frame <= 5 and not inrange: self.pickchain(Chains.Edgestall) return # We must be on the first frame, or else it's dangerous elif smashbot_state.action == Action.EDGE_HANGING and smashbot_state.action_frame == 1: if edgegrabframes > 29 and smashbot_state.invulnerability_left >= 29: self.pickchain(Chains.Edgestall) return # We are in danger of being attacked! # It's unsafe to be in shine range of opponent. We can't react to the attack! if self.gamestate.distance < 11.8 and opponent_state.character in [Character.FOX, Character.FALCO, Character.JIGGLYPUFF] and \ smashbot_state.invulnerability_left <= 1: # If we can, challenge their shine at the edge if self.difficulty >= 3 and edgegrabframes > 2: self.pickchain(Chains.Dropdownshine) return self.chain = None self.pickchain(Chains.DI, [0.5, 0.65]) return framesleft = Punish.framesleft(self.opponent_state, self.framedata) # Samus UP_B invulnerability samusupbinvuln = opponent_state.action in [Action.SWORD_DANCE_3_MID, Action.SWORD_DANCE_3_LOW] and \ opponent_state.character == Character.SAMUS and opponent_state.action_frame <= 5 # Shine them, as long as they aren't attacking right now frameadvantage = framesleft > 2 or smashbot_state.invulnerability_left > 2 if self.gamestate.distance < 11.8 and edgegrabframes > 2 and frameadvantage and not samusupbinvuln: self.pickchain(Chains.Dropdownshine) return # Illusion high if self.illusionhighframes() <= 5: if smashbot_state.invulnerability_left > 7: self.pickchain(Chains.Edgebair) return if self.firefoxhighframes() <= 5: self.pickchain(Chains.Edgebair) return # Do nothing self.chain = None self.pickchain(Chains.Nothing) return # We are on the stage else: edge_x = melee.stages.edgegroundposition(self.gamestate.stage) edgedistance = abs(edge_x - abs(self.smashbot_state.x)) randomgrab = False if random.randint(0, 20) == 0: randomgrab = True if self.difficulty == 4: randomgrab = True # Can we challenge their ledge? framesleft = Punish.framesleft(self.opponent_state, self.framedata) if not recoverhigh and not onedge and opponent_state.invulnerability_left < 5 and edgedistance < 10: if randomgrab or framesleft > 10: wavedash = True if self.framedata.isattack(opponent_state.character, opponent_state.action): wavedash = False self.pickchain(Chains.Grabedge, [wavedash]) return # Dash dance near the edge pivotpoint = opponent_state.x # Don't run off the stage though, adjust this back inwards a little if it's off edgebuffer = 5 pivotpoint = min(pivotpoint, edge_x - edgebuffer) pivotpoint = max(pivotpoint, (-edge_x) + edgebuffer) self.chain = None self.pickchain(Chains.DashDance, [pivotpoint])
def step(self, gamestate, smashbot_state, opponent_state): self._propagate = (gamestate, smashbot_state, opponent_state) recoverhigh = self.canrecoverhigh(gamestate, opponent_state) #If we can't interrupt the chain, just continue it if self.chain != None and not self.chain.interruptible: self.chain.step(gamestate, smashbot_state, opponent_state) return if Dropdownshine.inrange(smashbot_state, opponent_state, self.framedata): self.pickchain(Chains.Dropdownshine) return if smashbot_state.action == Action.EDGE_CATCHING: self.pickchain(Chains.Nothing) return # How many frames will it take to get to our opponent right now? onedge = smashbot_state.action in [ Action.EDGE_HANGING, Action.EDGE_CATCHING ] # Stand up if opponent attacks us proj_incoming = Defend.needsprojectiledefense( smashbot_state, opponent_state, gamestate) and smashbot_state.invulnerability_left <= 2 samusgrapple = opponent_state.character == Character.SAMUS and opponent_state.action == Action.SWORD_DANCE_4_LOW and \ -25 < opponent_state.position.y < 0 and smashbot_state.invulnerability_left <= 2 hitframe = self.framedata.in_range(opponent_state, smashbot_state, gamestate.stage) framesleft = hitframe - opponent_state.action_frame if proj_incoming or samusgrapple or ( hitframe != 0 and onedge and framesleft < 5 and smashbot_state.invulnerability_left < 2): # Unless the attack is a grab, then don't bother if not self.framedata.is_grab(opponent_state.character, opponent_state.action): if self.isupb(opponent_state): #TODO: Make this a chain self.chain = None self.controller.press_button(Button.BUTTON_L) return else: self.chain = None self.pickchain(Chains.DI, [0.5, 0.65]) return # For pikachu, we want to be up on the stage to edgeguard. Not on edge if opponent_state.character == Character.PIKACHU and smashbot_state.action == Action.EDGE_HANGING and smashbot_state.invulnerability_left == 0: if opponent_state.position.y < -20: self.chain = None self.pickchain(Chains.Edgedash, [False]) return # Special exception for Fox/Falco illusion # Since it is dumb and technically a projectile if opponent_state.character in [Character.FOX, Character.FALCO]: if opponent_state.action in [Action.SWORD_DANCE_2_MID]: self.chain = None self.pickchain(Chains.DI, [0.5, 0.65]) return # What recovery options does opponent have? landonstage = False grabedge = False # they have to commit to an up-b to recover mustupb = False canrecover = True djheight = self.framedata.dj_height(opponent_state) edgegrabframes = self.snaptoedgeframes(gamestate, opponent_state) # How heigh can they go with a jump? potentialheight = djheight + opponent_state.position.y if potentialheight < -23: mustupb = True # Now consider UP-B # Have they already UP-B'd? if self.isupb(opponent_state): if self.upbstart == 0: self.upbstart = opponent_state.position.y # If they are halfway through the up-b, then subtract out what they've alrady used potentialheight = self.upbheight(opponent_state) + self.upbstart elif opponent_state.action == Action.DEAD_FALL: potentialheight = opponent_state.position.y else: potentialheight += self.upbheight(opponent_state) # Cpt Falcon's up-b causes him to distort his model by a crazy amount. Giving him # the ability to get on the stage easier. Adjust for this adjustedheight = potentialheight if opponent_state.character == Character.CPTFALCON and self.isupb( opponent_state): adjustedheight += 12 # Adjust upwards a little to have some wiggle room if adjustedheight > -5: landonstage = True if potentialheight > -23: grabedge = True if potentialheight < -30: mustupb = True canrecover = False # Split the logic into two: # A) We are on the edge # B) We are on the stage if smashbot_state.action in [ Action.EDGE_HANGING, Action.EDGE_CATCHING ]: # If opponent can't recover, then just get onto the stage! if not canrecover: #TODO: Make this a chain self.chain = None self.controller.press_button(Button.BUTTON_L) return # Don't roll up too early for Falcon falconupearly = opponent_state.character == Character.CPTFALCON and \ opponent_state.action == Action.SWORD_DANCE_3_LOW and opponent_state.action_frame <= 12 # Roll up to edgehog if self.isupb( opponent_state) and not landonstage and not falconupearly: #TODO: Make this a chain self.chain = None self.controller.press_button(Button.BUTTON_L) return # Challenge rising UP-B's with a shine if we're in range # except for pikachu and falcon/ganon if self.isupb( opponent_state ) and opponent_state.speed_y_self >= 0 and gamestate.distance < 10: if opponent_state.character not in [ Character.PIKACHU, Character.GANONDORF, Character.CPTFALCON ]: self.pickchain(Chains.Dropdownshine) return # Edgestall # For Fox and Falco, we have a different edgestall strategy. Only edgestall if they start a FireFox if opponent_state.character in [Character.FOX, Character.FALCO]: # Are they in the start of a firefox? # But make sure they can't grab the edge in the middle of it edgedistance = abs(opponent_state.position.x) - ( melee.stages.EDGE_GROUND_POSITION[gamestate.stage] + 15) in_immediate_range = (-5 > opponent_state.position.y > -23) and (edgedistance < 15) in_fly_range = opponent_state.action_frame > ( (edgedistance - 15) / 3) if opponent_state.action == Action.SWORD_DANCE_3_LOW and not in_fly_range and not in_immediate_range: self.pickchain(Chains.Edgestall) return # We must be on the first frame, or else it's dangerous elif smashbot_state.action == Action.EDGE_HANGING and smashbot_state.action_frame == 1: if edgegrabframes > 29 and smashbot_state.invulnerability_left >= 29: self.pickchain(Chains.Edgestall) return # We are in danger of being attacked! # It's unsafe to be in shine range of opponent. We can't react to the attack! if gamestate.distance < 11.8 and opponent_state.character in [Character.FOX, Character.FALCO, Character.JIGGLYPUFF] and \ smashbot_state.invulnerability_left <= 1: # If we can, challenge their shine at the edge if self.difficulty >= 3 and edgegrabframes > 2: if Dropdownshine.inrange(smashbot_state, opponent_state, self.framedata): self.pickchain(Chains.Dropdownshine) return self.chain = None self.pickchain(Chains.DI, [0.5, 0.65]) return framesleft = Punish.framesleft(opponent_state, self.framedata, smashbot_state) # Samus UP_B invulnerability samusupbinvuln = opponent_state.action in [Action.SWORD_DANCE_3_MID, Action.SWORD_DANCE_3_LOW] and \ opponent_state.character == Character.SAMUS and opponent_state.action_frame <= 5 # Shine them, as long as they aren't attacking right now frameadvantage = framesleft > 2 or smashbot_state.invulnerability_left > 2 if gamestate.distance < 11.8 and edgegrabframes > 2 and frameadvantage and not samusupbinvuln: if Dropdownshine.inrange(smashbot_state, opponent_state, self.framedata): self.pickchain(Chains.Dropdownshine) return # Illusion high if self.illusionhighframes(gamestate, opponent_state) <= 5: if smashbot_state.invulnerability_left > 7: self.pickchain(Chains.Edgebair) return # If opponent is recovering high with illusion, bair them at the right time if (opponent_state.character == Character.FOX and opponent_state.action_frame == 15) or (opponent_state.character == Character.FALCO and opponent_state.action_frame == 10): if opponent_state.action == Action.SWORD_DANCE_2_HIGH and opponent_state.position.y > -4: self.pickchain(Chains.Edgebair) return if self.firefoxhighframes(gamestate, opponent_state) <= 5: self.pickchain(Chains.Edgebair) return # Do nothing self.chain = None self.pickchain(Chains.Nothing) return # We are on the stage else: edge_x = melee.stages.EDGE_GROUND_POSITION[gamestate.stage] edgedistance = abs(edge_x - abs(smashbot_state.position.x)) randomgrab = False if random.randint(0, 20) == 0: randomgrab = True # Don't make this guaranteed, even on most aggressive mode. Make it common, but not predictable if self.difficulty == 4 and random.randint(0, 10) == 0: randomgrab = True # For pikachu and jiggs don't grab the edge unless they're sitting, camping if opponent_state.character in [ Character.PIKACHU, Character.JIGGLYPUFF ] and opponent_state.action != Action.EDGE_HANGING: randomgrab = False # TODO Don't grab the edge if opponent is # They're camping. Camp back if gamestate.custom["ledge_grab_count"] > 3: # Get into position away from the edge. pivotpoint = 0 if abs(smashbot_state.position.x - pivotpoint) > 5: self.chain = None self.pickchain(Chains.DashDance, [pivotpoint]) return elif len(gamestate.projectiles) == 0: # Laser self.pickchain(Chains.Laser) return # Can we challenge their ledge? framesleft = Punish.framesleft(opponent_state, self.framedata, smashbot_state) # Sheik shino stall is safe to grab edge from on these frames if opponent_state.character == Character.SHEIK and opponent_state.action == Action.SWORD_DANCE_1_AIR and opponent_state.action_frame < 5: self.pickchain(Chains.Grabedge, [True]) return # Puff sing stall is safe to grab from if opponent_state.character == Character.JIGGLYPUFF and opponent_state.action in [ Action.DOWN_B_AIR, Action.SHINE_RELEASE_AIR ] and opponent_state.action_frame < 10: self.pickchain(Chains.Grabedge, [True]) return # Grab edge out from under Pika quick-attack startup if opponent_state.character == Character.PIKACHU and opponent_state.action == Action.SWORD_DANCE_4_MID and opponent_state.action_frame < 7: self.pickchain(Chains.Grabedge, [True]) return # Grab the edge when opponent starts a FireFox if opponent_state.character in [ Character.FOX, Character.FALCO ] and opponent_state.action == Action.SWORD_DANCE_3_LOW and ( opponent_state.action_frame < 22): # But not if they're in range to grab the edge themselves edgedistance = abs(opponent_state.position.x) - ( melee.stages.EDGE_GROUND_POSITION[gamestate.stage] + 15) in_immediate_range = (-5 > opponent_state.position.y > -28) and (edgedistance < 15) if not in_immediate_range: self.pickchain(Chains.Grabedge, [True]) return if ( not recoverhigh or randomgrab ) and not onedge and opponent_state.invulnerability_left < 5 and edgedistance < 10 and smashbot_state.on_ground: if (randomgrab or framesleft > 10) and opponent_state.action not in [ Action.EDGE_ROLL_SLOW, Action.EDGE_ROLL_QUICK, Action.EDGE_GETUP_SLOW, Action.EDGE_GETUP_QUICK, Action.EDGE_ATTACK_SLOW, Action.EDGE_ATTACK_QUICK ]: if not self.framedata.is_attack(opponent_state.character, opponent_state.action): ff_early = False if opponent_state.character in [ Character.FOX, Character.FALCO ] and opponent_state.action == Action.SWORD_DANCE_3_LOW: if opponent_state.action_frame < 20: ff_early = True if not ff_early: self.pickchain(Chains.Grabedge, [True]) return # Dash dance near the edge pivotpoint = opponent_state.position.x # Don't run off the stage though, adjust this back inwards a little if it's off edgebuffer = 2 # Against Jigglypuff, we need to respect the ledge invulnerability. DD inwards more if opponent_state.character == Character.JIGGLYPUFF and opponent_state.invulnerability_left > 0: if self.logger: self.logger.log("Notes", "staying safe: " + str(opponent_state.invulnerability_left) + " ", concat=True) if opponent_state.position.x > 0: pivotpoint -= 10 else: pivotpoint += 10 pivotpoint = min(pivotpoint, edge_x - edgebuffer) pivotpoint = max(pivotpoint, (-edge_x) + edgebuffer) self.chain = None self.pickchain(Chains.DashDance, [pivotpoint])
def step(self): # If we have stopped approaching, reset the state if type(self.tactic) != Tactics.Approach: self.approach = False if Mitigate.needsmitigation(self.smashbot_state): self.picktactic(Tactics.Mitigate) return if self.tactic and not self.tactic.isinteruptible(): self.tactic.step() return # If we're stuck in a lag state, just do nothing. Trying an action might just # buffer an input we don't want if Wait.shouldwait(self.smashbot_state, self.framedata): self.picktactic(Tactics.Wait) return if Recover.needsrecovery(self.smashbot_state, self.opponent_state, self.gamestate): self.picktactic(Tactics.Recover) return if Celebrate.deservescelebration(self.smashbot_state, self.opponent_state): self.picktactic(Tactics.Celebrate) return # Difficulty 5 is a debug / training mode # Don't do any attacks, and don't do any shielding # Take attacks, DI, and recover if self.difficulty == 5: self.picktactic(Tactics.KeepDistance) return if Defend.needsprojectiledefense(self.smashbot_state, self.opponent_state, self.gamestate): self.picktactic(Tactics.Defend) return # If we can infinite our opponent, do that! if Infinite.caninfinite(self.smashbot_state, self.opponent_state, self.gamestate, self.framedata, self.difficulty): self.picktactic(Tactics.Infinite) return # If we can punish our opponent for a laggy move, let's do that if Punish.canpunish(self.smashbot_state, self.opponent_state, self.gamestate, self.framedata): self.picktactic(Tactics.Punish) return # Do we need to defend an attack? if Defend.needsdefense(self.smashbot_state, self.opponent_state, self.gamestate, self.framedata): self.picktactic(Tactics.Defend) return # Can we edge guard them? if Edgeguard.canedgeguard(self.smashbot_state, self.opponent_state, self.gamestate): self.picktactic(Tactics.Edgeguard) return # Can we shield pressure them? if Pressure.canpressure(self.opponent_state, self.gamestate): self.picktactic(Tactics.Pressure) return if Retreat.shouldretreat(self.smashbot_state, self.opponent_state, self.gamestate): self.picktactic(Tactics.Retreat) return # Is opponent starting a jump? jumping = self.opponent_state.action == Action.KNEE_BEND if self.opponent_state.action in [Action.JUMPING_FORWARD, Action.JUMPING_BACKWARD] and \ self.opponent_state.speed_y_self > 0: jumping = True # Randomly approach some times rather than keeping distance if self.smashbot_state.action == Action.TURNING and random.randint( 0, 40) == 0: self.approach = True if (jumping and self.opponent_state.invulnerability_left <= 0 ) or self.approach: self.picktactic(Tactics.Approach) return self.picktactic(Tactics.KeepDistance)
def step(self, gamestate, smashbot_state, opponent_state): self._propagate = (gamestate, smashbot_state, opponent_state) opponentonedge = opponent_state.action in [Action.EDGE_HANGING, Action.EDGE_CATCHING, Action.EDGE_GETUP_SLOW, \ Action.EDGE_GETUP_QUICK, Action.EDGE_ATTACK_SLOW, Action.EDGE_ATTACK_QUICK, Action.EDGE_ROLL_SLOW, Action.EDGE_ROLL_QUICK] # If we can't interrupt the chain, just continue it if self.chain != None and not self.chain.interruptible: self.chain.step(gamestate, smashbot_state, opponent_state) return # TODO: Determine whether we should refresh before the ledge dash # It takes 16 frames to go from frame 1 of hanging to standing. frames_left = Punish.framesleft(opponent_state, self.framedata, smashbot_state) refresh = False if smashbot_state.action in [ Action.EDGE_HANGING, Action.EDGE_CATCHING ]: self.pickchain(Chains.Edgedash, [refresh]) return # If we can't possibly illusion to recover, don't try if smashbot_state.position.y < -15 and smashbot_state.jumps_left == 0 and smashbot_state.speed_y_self < 0: self.useillusion = False diff_x = abs(melee.stages.EDGE_POSITION[gamestate.stage] - abs(smashbot_state.position.x)) # If we can just grab the edge with a firefox, do that facinginwards = smashbot_state.facing == (smashbot_state.position.x < 0) if not facinginwards and smashbot_state.action == Action.TURNING and smashbot_state.action_frame == 1: facinginwards = True if smashbot_state.action == Action.DEAD_FALL: x = 0 if smashbot_state.position.x < 0: x = 1 self.chain = None self.pickchain(Chains.DI, [x, 0.5]) return # Are we facing the wrong way in shine? Turn around if smashbot_state.action == Action.DOWN_B_STUN and not facinginwards: x = 0 if smashbot_state.position.x < 0: x = 1 self.chain = None self.pickchain(Chains.DI, [x, 0.5]) return # If we can just do nothing and grab the edge, do that # Action.SWORD_DANCE_1_AIR is Fox's initial freefall after his upB finishes launching. # Fox can ledgegrab from behind in this animation, but he oftentimes needs to fastfall to hit the window. if -12 < smashbot_state.position.y and (diff_x < 10) and ( facinginwards or smashbot_state.action == Action.SWORD_DANCE_1_AIR ) and smashbot_state.speed_y_self < 0: # Do a Fastfall if we're not already if smashbot_state.action == Action.FALLING and smashbot_state.speed_y_self > -3.3: self.chain = None self.pickchain(Chains.DI, [0.5, 0]) return # If we are currently moving away from the stage, DI in if (smashbot_state.speed_air_x_self > 0) == (smashbot_state.position.x > 0): x = 0 if smashbot_state.position.x < 0: x = 1 self.chain = None self.pickchain(Chains.DI, [x, 0.5]) return else: self.pickchain(Chains.Nothing) return # If we illusion at this range when the opponent is holding ledge, Smashbot dies. # Firefox instead if the opponent is grabbing edge. Opponent has to get up or get burned. if (-16.4 < smashbot_state.position.y < -5) and (diff_x < 10) and facinginwards and opponentonedge: if gamestate.stage == melee.enums.Stage.BATTLEFIELD: # If Smashbot does a random or horizontal sideB here, he pretty reliably SDs on Battlefield self.pickchain(Chains.Firefox, [FIREFOX.EDGE]) else: self.pickchain(Chains.Firefox, [FIREFOX.RANDOM]) return # If we're lined up, do the illusion # 88 is a little longer than the illusion max length if self.useillusion and (-16.4 < smashbot_state.position.y < -5) and ( 10 < diff_x < 88) and not opponentonedge: length = SHORTEN.LONG if diff_x < 50: length = SHORTEN.MID if diff_x < 40: length = SHORTEN.MID_SHORT if diff_x < 31: length = SHORTEN.SHORT self.pickchain(Chains.Illusion, [length]) return # Is the opponent going offstage to edgeguard us? opponent_edgedistance = abs(opponent_state.position.x) - abs( melee.stages.EDGE_GROUND_POSITION[gamestate.stage]) opponentxvelocity = opponent_state.speed_air_x_self + opponent_state.speed_ground_x_self opponentmovingtoedge = not opponent_state.off_stage and ( opponent_edgedistance < 20) and (opponentxvelocity > 0 == opponent_state.position.x > 0) opponentgoingoffstage = opponent_state.action in [Action.FALLING, Action.JUMPING_FORWARD, Action.JUMPING_BACKWARD, Action.LANDING_SPECIAL,\ Action.DASHING, Action.WALK_MIDDLE, Action.WALK_FAST, Action.NAIR, Action.FAIR, Action.UAIR, Action.BAIR, Action.DAIR] # Don't airdodge recovery if we still have attack velocity. It just causes an SD hit_movement = abs(smashbot_state.speed_x_attack) > 0.2 x_canairdodge = abs(smashbot_state.position.x) - 18 <= abs( melee.stages.EDGE_GROUND_POSITION[gamestate.stage]) y_canairdodge = smashbot_state.position.y + 18 >= -6 # airdodge_randomizer not currently in use airdodge_randomizer = random.randint(0, 4) == 1 if x_canairdodge and y_canairdodge and ( opponentgoingoffstage or opponentmovingtoedge) and not hit_movement: self.pickchain(Chains.Airdodge, [ int(smashbot_state.position.x < 0), int(smashbot_state.position.y + smashbot_state.ecb.bottom.y < 5) ]) return # First jump back to the stage if we're low # Fox can at least DJ from y = -55.43 and still sweetspot grab the ledge. # For reference, if Fox inputs a DJ at y = -58.83, he will NOT sweetspot grab the ledge. jump_randomizer = random.randint(0, 5) == 1 if smashbot_state.jumps_left > 0 and (smashbot_state.position.y < -52 or opponentgoingoffstage or opponentmovingtoedge or jump_randomizer): self.pickchain(Chains.Jump) return # If we're high and doing an Illusion, just let ourselves fall into place if self.useillusion and smashbot_state.position.y > -5: # DI into the stage x = 0 if smashbot_state.position.x < 0: x = 1 self.chain = None self.pickchain(Chains.DI, [x, 0.5]) return # Don't firefox if we're super high up, wait a little to come down if smashbot_state.speed_y_self < 0 and smashbot_state.position.y < -60: if gamestate.stage == melee.enums.Stage.BATTLEFIELD and diff_x < 30: self.pickchain(Chains.Firefox, [FIREFOX.HIGH]) else: self.pickchain(Chains.Firefox, [FIREFOX.SAFERANDOM]) return randomhighrecovery = smashbot_state.position.y > 0 and random.randint( 0, 3) == 1 if randomhighrecovery: if bool(random.randint(0, 1)): self.pickchain(Chains.Firefox, [FIREFOX.RANDOM]) else: self.pickchain(Chains.Illusion, [SHORTEN.LONG]) return # DI into the stage battlefielded = ( smashbot_state.position.x < melee.stages.EDGE_POSITION[gamestate.stage] + 13 ) and gamestate.stage == melee.enums.Stage.BATTLEFIELD and smashbot_state.position.y < 0 if not battlefielded: x = 0 if smashbot_state.position.x < 0: x = 1 self.chain = None self.pickchain(Chains.DI, [x, 0.5])
def step(self, gamestate, smashbot_state, opponent_state): self._propagate = (gamestate, smashbot_state, opponent_state) #If we can't interrupt the chain, just continue it if self.chain != None and not self.chain.interruptible: self.chain.step(gamestate, smashbot_state, opponent_state) return framesleft = Punish.framesleft(opponent_state, self.framedata) # This is off by one for hitstun framesleft -= 1 shinerange = 9.9 if smashbot_state.action == Action.RUNNING: shinerange = 12.8 if smashbot_state.action == Action.DASHING: shinerange = 9.5 # If we shine too close to the edge while accelerating horizontally, we can slide offstage and get into trouble distance_from_edge = melee.stages.EDGE_GROUND_POSITION[ gamestate.stage] - abs(smashbot_state.x) edgetooclose = (smashbot_state.action == Action.EDGE_TEETERING_START or distance_from_edge < 5) \ or (smashbot_state.action in [Action.RUNNING, Action.RUN_BRAKE, Action.CROUCH_START] and distance_from_edge < 10.5) # Try to do the shine if gamestate.distance < shinerange and not edgetooclose: # emergency backup shine if framesleft == 1: self.chain = None self.pickchain(Chains.Waveshine) return # We always want to try to shine our opponent towards the center of the stage # If we are lined up right now, do the shine if (smashbot_state.x < opponent_state.x < 0) or (0 < opponent_state.x < smashbot_state.x): self.chain = None self.pickchain(Chains.Waveshine) return # If we are running away from our opponent, just shine now onright = opponent_state.x < smashbot_state.x if (smashbot_state.speed_ground_x_self > 0) == onright: self.chain = None self.pickchain(Chains.Waveshine) return if smashbot_state.action == Action.LANDING_SPECIAL and smashbot_state.action_frame < 28: self.pickchain(Chains.Nothing) return if not smashbot_state.action == Action.DOWN_B_GROUND_START and smashbot_state.action_frame in [ 1, 2 ]: # In general, we want to run the direction the opponent is moving # Unless we're out of range and on the opposite side. if gamestate.distance > shinerange and ( (smashbot_state.x < opponent_state.x < 0) or (0 < opponent_state.x < smashbot_state.x)): self.pickchain(Chains.Run, [opponent_state.x > smashbot_state.x]) else: self.pickchain(Chains.Run, [opponent_state.speed_x_attack > 0]) return