def start_handoff_actions(self, game: botbowl.Game, action_choice) -> ActionProbList: ball = game.get_ball() if ball is not None and ball.is_carried: if ball.position in action_choice.positions: return [(Action(ActionType.START_HANDOFF, position=[ball.position]), 1)] return []
def move_actions(self, game: botbowl.Game, action_choice) -> ActionProbList: if game.get_player_action_type( ) in self.player_actiontypes_without_move_actions: return [] action_probs = [] player = game.get_active_player() ball_carrier = game.get_ball_carrier() is_ball_carrier = player is game.get_ball_carrier() if player.state.moves > 0 and not is_ball_carrier: return [] elif is_ball_carrier and player.state.moves > 0: action_probs.append((Action(ActionType.END_PLAYER_TURN), 1)) ball = game.get_ball() ball_on_floor_pos = game.get_ball( ).position if not ball.is_carried else None opp_ball_carrier = ball_carrier if ball_carrier is not None and ball_carrier.team is not game.active_team else None is_home = player.team is game.state.home_team for pos in action_choice.positions: prob = 1 if ball_on_floor_pos is not None: if pos == ball_on_floor_pos: prob = 3 elif pos.distance(ball_on_floor_pos) == 1: prob = 2 elif opp_ball_carrier is not None and pos.distance( opp_ball_carrier.position): prob = 2 elif is_ball_carrier and game.arena.is_in_opp_endzone( pos, is_home): prob = 2 action = Action(ActionType.MOVE, position=pos) action_probs.append((action, prob)) return action_probs
def get_heuristic(game: botbowl.Game) -> HeuristicVector: """ Heuristic based on game state, calculated from home teams perspective zero sum, meaning away team's heuristic is negative of home team's heuristic :returns: array with different heuristics, multiply it with """ score, tv_on_pitch, ball_position, ball_carried, ball_marked = 0.0, 0.0, 0.0, 0.0, 0.0 home = game.state.home_team away = game.state.away_team score += home.state.score - away.state.score tv_on_pitch += sum(p.role.cost for p in game.get_players_on_pitch(team=home)) tv_on_pitch -= sum(p.role.cost for p in game.get_players_on_pitch(team=away)) tv_on_pitch /= 50000.0 # normalized to cost of lineman ball = game.get_ball() if ball is not None and ball.position is not None: ball_position -= ball.position.x # negative because home team scores at x = 0 home_marking_ball = len( game.get_adjacent_players(ball.position, team=home, standing=True)) > 0 away_marking_ball = len( game.get_adjacent_players(ball.position, team=away, standing=True)) > 0 if ball.is_carried: ball_carrier = game.get_player_at(ball.position) if ball_carrier.team == home: ball_carried += 1 ball_marked -= away_marking_ball elif ball_carrier.team == away: ball_carried -= 1 ball_marked += home_marking_ball else: ball_marked += (home_marking_ball - away_marking_ball) return HeuristicVector(score=score, tv_on_pitch=tv_on_pitch, ball_position=ball_position, ball_carried=ball_carried, ball_marked=ball_marked)
def expand_moving(game: botbowl.Game, parent: Node) -> Node: # noinspection PyTypeChecker active_proc: Union[procedures.GFI, procedures.Dodge] = game.get_procedure() assert type(active_proc) is procedures.Dodge or type( active_proc) is procedures.GFI move_action_proc: procedures.MoveAction = first( proc for proc in reversed(game.state.stack.items) if isinstance(proc, procedures.MoveAction)) is_blitz = type(move_action_proc) is procedures.BlitzAction is_handoff = type(move_action_proc) is procedures.HandoffAction player = move_action_proc.player if move_action_proc.steps is not None: final_step = move_action_proc.steps[-1] else: if is_blitz: block_proc: procedures.Block = first( filter(lambda proc: type(proc) is procedures.Block, game.state.stack.items)) final_step = block_proc.defender.position elif is_handoff: raise ValueError() else: final_step = active_proc.position is_pickup = game.get_ball( ).position == final_step and not game.get_ball().is_carried path = move_action_proc.paths[final_step] """ This block of code sets two important variables: probability_success - probability of the remaining path rolls - list[int] - the remaining rolls of the path Normal case we just fetch this from the path object. If we're in a rerolled proc, it's nasty... """ if active_proc.roll is None: probability_success = path.prob rolls = list(collapse(path.rolls)) if is_pickup: # remove the pickup roll and probability rolls.pop() probability_success /= game.get_pickup_prob( active_proc.player, final_step) else: with only_fixed_rolls(game): game.step() new_proc = game.get_procedure() if type(new_proc) not in {procedures.GFI, procedures.Dodge}: assert not active_proc.reroll.use_reroll return expand_none_action(game, parent) # if we get here, it means that a reroll was used. assert new_proc is active_proc assert active_proc.roll is None assert active_proc.reroll is None current_step = active_proc.position try: assert player.position.distance( current_step) == 1 or is_pickup or is_blitz except AssertionError as e: raise e i = 0 while path.steps[i] != current_step: i += 1 remaining_current_step_rolls = path.rolls[i][:] if is_pickup and current_step == final_step: remaining_current_step_rolls.pop() num_current_step_remaining_rolls = len( list( filter(lambda x: type(x) in {procedures.GFI, procedures.Dodge}, game.state.stack.items))) assert num_current_step_remaining_rolls in [1, 2] remaining_current_step_rolls = remaining_current_step_rolls[ -num_current_step_remaining_rolls:] probability_success = reduce( operator.mul, map(lambda d: (7 - d) / 6, remaining_current_step_rolls), 1.0) rolls = list(collapse(remaining_current_step_rolls)) if current_step != final_step: step_count = game.get_step() if player.position != current_step: try: game.move(player, current_step) except AssertionError as e: raise e new_path = pf.get_safest_path(game, player, final_step, blitz=is_blitz) game.revert(step_count) #try: # # assert new_path.steps == path.steps[-len(new_path):] this assert can't be made because of small randomness in pathfinder # assert list(collapse(new_path.rolls)) == list(collapse(path.rolls[-len(new_path):])), f"{new_path.rolls} != {path.rolls[-len(new_path):]}" #except AssertionError as e: # raise e try: rolls.extend(collapse(new_path.rolls)) except AttributeError as e: raise e probability_success *= new_path.prob if is_pickup: # remove the pickup roll and probability rolls.pop() probability_success /= game.get_pickup_prob( active_proc.player, final_step) try: p = np.array(rolls) / sum(rolls) index_of_failure = np.random.choice(range(len(rolls)), 1, p=p)[0] except ValueError as e: raise e # STEP UNTIL FAILURE (possibly no steps at all) with only_fixed_rolls(game, d6=[6] * index_of_failure): while len(botbowl.D6.FixedRolls) > 0: if len(game.get_available_actions()) > 0: raise AttributeError("wrong") game.step() new_parent = ChanceNode(game, parent) debug_step_count = game.get_step() # SUCCESS SCENARIO with only_fixed_rolls(game, d6=[6] * (len(rolls) - index_of_failure)): while len(botbowl.D6.FixedRolls) > 0: if type(game.get_procedure()) not in { procedures.GFI, procedures.Block, procedures.Dodge, procedures.Move, procedures.MoveAction, procedures.BlitzAction }: raise AttributeError("wrong") if len(game.get_available_actions()) > 0: raise AttributeError("wrong") if type(game.get_procedure() ) is procedures.Block and not game.get_procedure().gfi: raise AttributeError("wrong") game.step() success_node = expand_none_action(game, new_parent, moving_handled=True) new_parent.connect_child(success_node, probability_success) assert debug_step_count == game.get_step() # FAILURE SCENARIO with only_fixed_rolls(game, d6=[1]): while len(botbowl.D6.FixedRolls) > 0: if len(game.get_available_actions()) > 0: raise AttributeError("wrong") game.step() fail_node = expand_none_action(game, new_parent, moving_handled=True) new_parent.connect_child(fail_node, 1 - probability_success) assert debug_step_count == game.get_step() return new_parent