"""
# Installation
"""
# Install:
# Kaggle environments.
!git clone https://github.com/Kaggle/kaggle-environments.git
!cd kaggle-environments && pip install .

# GFootball environment.
!apt-get update -y
!apt-get install -y libsdl2-gfx-dev libsdl2-ttf-dev

# Make sure that the Branch in git clone and in wget call matches !!
!git clone -b v2.3 https://github.com/google-research/football.git
!mkdir -p football/third_party/gfootball_engine/lib

!wget https://storage.googleapis.com/gfootball/prebuilt_gameplayfootball_v2.3.so -O football/third_party/gfootball_engine/lib/prebuilt_gameplayfootball.so
!cd football && GFOOTBALL_USE_PREBUILT_SO=1 pip3 install .
"""
# Smart Control Strategy

## Theory

I'm glad to introduce some basic strategy that you may use as basis for your future super-strategy! The aim of this strategy is to move only to regions without enemy football players. That's what I call **smart controll strategy**. More details:

1. Suggestion about distance of football step. You may use my [topic](https://www.kaggle.com/c/google-football/discussion/187794) about it, but I've used a suggestion that it takes 0.1 ($STEP\_HARD\_DIST$) and 0.05 ($STEP\_EASY\_DIST$) for running step and for usual step correspondingly. I do it, cause I've decided to count ball touches by players instead of game steps. It seems like we need about ten running step to achieve target from the middle of the football field (simple suggestion from the leaderboard).

2. After first stage we can estimate, where we would be after each movement action. I call this points as $step\_point$ (so, one $step\_point$ for left action, one $step\_point$ for right action, etc.)

3. For each $step\_point$ we can count distances for each of enemy football player. If the mininmum of this distances is less, than $SAFE\_DIST$, we consider $step\_point$ as safe point.

4. From each $step\_point$ we choose the nearest to the enemy target. Corresponding actions is a choosen action.

5. Shoot in the last quarter of the football field.
"""
"""
## Implementation
"""
%%writefile submission.py
from kaggle_environments.envs.football.helpers import *

@human_readable_agent
def agent(obs):
    import numpy as np
    import pandas as pd
    from pandas import Series, DataFrame
    
    ###### 0. CONSTANTS ######
    
    ENEMY_TARGET = [ 1, 0]
    OWN_TARGET   = [-1, 0]

    STEP_HARD_DIST = 0.1
    STEP_EASY_DIST = 0.5 * STEP_HARD_DIST
    SAFE_DIST = 0.1

    ###### 1. SMART CONTROL: FUNCTIONS ######

    def get_action_steps(step_dist):
        import numpy as np

        return {
            Action.Idle:          [                                0,                                 0],
            Action.Left:          [step_dist * np.cos(        np.pi), step_dist * np.sin(        np.pi)],
            Action.TopLeft:       [step_dist * np.cos( 0.75 * np.pi), step_dist * np.sin( 0.75 * np.pi)],
            Action.Top:           [step_dist * np.cos( 0.5  * np.pi), step_dist * np.sin( 0.5  * np.pi)],
            Action.TopRight:      [step_dist * np.cos( 0.25 * np.pi), step_dist * np.sin( 0.25 * np.pi)],
            Action.Right:         [step_dist * np.cos(            0), step_dist * np.sin(            0)],
            Action.BottomRight:   [step_dist * np.cos(-0.25 * np.pi), step_dist * np.sin(-0.25 * np.pi)],
            Action.Bottom:        [step_dist * np.cos(-0.5  * np.pi), step_dist * np.sin(-0.5  * np.pi)],
            Action.BottomLeft:    [step_dist * np.cos(-0.75 * np.pi), step_dist * np.sin(-0.75 * np.pi)]
        }


    def get_point_point_dist(point0, point):
        import numpy as np

        x0, y0 = point0[0], point0[1]
        x, y = point[0], point[1]
        return np.sqrt((x0 - x) ** 2 + (y0 - y) ** 2)


    def correct_point(point0):
        x, y = point0[0], point0[1]
        return (-1 <= x <= 1) and (-1 <= y <= 1)


    def get_move_action_info(point0, enemy_points, step_dist=STEP_EASY_DIST):
        import numpy as np

        target_dists = {}
        for action, step in get_action_steps(step_dist).items():
            step_point = [point0[0] + step[0], point0[1] + step[1]]
            if correct_point(step_point):
                ## 1. Enemy min distance
                enemy_distances = [
                    get_point_point_dist(step_point, enemy_point)
                    for enemy_point in enemy_points
                ]
                enemy_dist = np.array(enemy_distances).min()

                ## 2. Target distance
                target_dist = get_point_point_dist(step_point, ENEMY_TARGET)

                target_dists[action] = {
                    "target_dist" : round(target_dist, 3),
                    "enemy_dist" : round(enemy_dist, 3)
                }

        return target_dists


    def get_best_move_action(get_move_action_info, safe_dist=SAFE_DIST):
        from pandas import Series

        safe_actions = {}
        for action, info in get_move_action_info.items():
            target_dist, enemy_dist = info["target_dist"], info["enemy_dist"]
            if enemy_dist >= SAFE_DIST:
                safe_actions[action] = target_dist

        if len(safe_actions) == 0:
            return Action.Right ### fix in the future

        target_action = Series(safe_actions).idxmin()

        return target_action


    def make_decision(point0, move_action_info):
        x0, y0 = point0[0], point0[1]

        ## Shot decision
        if x0 >= 0.5:
            return Action.Shot

        ## Move decision
        best_move_action = get_best_move_action(move_action_info)
        return best_move_action
    
    
    ###### 2. GAME START ######
    
    own_points = obs['left_team']
    enemy_points = obs['right_team']
    point0 = obs['left_team'][obs['active']]
    
    if Action.Sprint not in obs['sticky_actions']:
        return Action.Sprint

    if obs['ball_owned_player'] == obs['active'] and obs['ball_owned_team'] == 0:
        ###### OFFENSE: SMART CONTROL ######
        move_action_info = get_move_action_info(point0, enemy_points)
        return make_decision(point0, move_action_info)
    else:
        ###### DEFENCE: OLD STRATEGY ######
        if obs['ball'][0] > point0[0] + 0.05:
            return Action.Right
        if obs['ball'][0] < point0[0] - 0.05:
            return Action.Left
        if obs['ball'][1] > point0[1] + 0.05:
            return Action.Bottom
        if obs['ball'][1] < point0[1] - 0.05:
            return Action.Top
        return Action.Slide
# Set up the Environment.
from kaggle_environments import make

env = make(
    "football",
    configuration={
        "save_video": True,
        "scenario_name": "11_vs_11_kaggle",
        "running_in_notebook": True
    }
)

output = env.run(["/kaggle/working/submission.py", "/kaggle/working/submission.py"])[-1]

print('Left player: reward = %s, status = %s, info = %s' % (output[0]['reward'], output[0]['status'], output[0]['info']))
print('Right player: reward = %s, status = %s, info = %s' % (output[1]['reward'], output[1]['status'], output[1]['info']))
env.render(mode="human", width=800, height=600)