{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install --target=lib --no-deps stable-baselines3 gym","metadata":{"execution":{"iopub.status.busy":"2022-07-02T12:18:39.372204Z","iopub.execute_input":"2022-07-02T12:18:39.373602Z","iopub.status.idle":"2022-07-02T12:19:08.156558Z","shell.execute_reply.started":"2022-07-02T12:18:39.373517Z","shell.execute_reply":"2022-07-02T12:19:08.154912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nos.makedirs('model')","metadata":{"execution":{"iopub.status.busy":"2022-07-03T11:44:17.219987Z","iopub.execute_input":"2022-07-03T11:44:17.221021Z","iopub.status.idle":"2022-07-03T11:44:17.224989Z","shell.execute_reply.started":"2022-07-03T11:44:17.220968Z","shell.execute_reply":"2022-07-03T11:44:17.224206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cp -r ../input/drl-ga-starter ./model","metadata":{"execution":{"iopub.status.busy":"2022-07-03T11:46:28.081143Z","iopub.execute_input":"2022-07-03T11:46:28.081963Z","iopub.status.idle":"2022-07-03T11:46:29.180487Z","shell.execute_reply.started":"2022-07-03T11:46:28.081913Z","shell.execute_reply":"2022-07-03T11:46:29.179345Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!zip -r model.zip model","metadata":{"execution":{"iopub.status.busy":"2022-07-03T11:50:41.203819Z","iopub.execute_input":"2022-07-03T11:50:41.204207Z","iopub.status.idle":"2022-07-03T11:50:42.406227Z","shell.execute_reply.started":"2022-07-03T11:50:41.204169Z","shell.execute_reply":"2022-07-03T11:50:42.404655Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile environment.py\n# Imports\nfrom random import choice\nfrom typing import Union\n\nfrom gym import Env, spaces\nfrom kaggle_environments import make\nfrom kaggle_environments.envs.kore_fleets.helpers import (\n    Board,\n    Configuration,\n    Observation,\n    ShipyardAction,\n)\nimport numpy as np\nfrom tensorflow.keras import models, layers\n\n\nclass CustomKoreEnv(Env):\n    \"\"\"\n    This is a custom Kore environment, adapted for use with\n    OpenAI Gym. We don't actually need to use OpenAI Gym in\n    this example, but I've chosen to do it for the sake of\n    compatibility for those who already have code for it.\n    \"\"\"\n\n    metadata = {\"render.modes\": [\"human\"]}\n\n    def __init__(self, agent2=\"random\"):\n        super().__init__()\n        # Initialize the actual environment\n        kore_env = make(\"kore_fleets\", debug=True)\n        self.env = kore_env.train([None, agent2])\n        self.env_configuration: Configuration = kore_env.configuration\n        map_size = self.env_configuration.size\n        self.board: Board = None\n\n        # Our observation space will be a matrix of size\n        # (map_size, map_size, 4).\n        #\n        # - The first layer is the kore count, which has\n        #   its values mapped to [0, 1]\n        #\n        # - The second layer is the fleet size, which has\n        #   its values mapped to [-1, 1] (negative values\n        #   are used to represent enemy fleets)\n        #\n        # - The third layer represents possible places that\n        #   the enemy fleets can be at the next turn. All\n        #   values are either -1 (enemy can be there) or\n        #   0 (enemy can't be there).\n        #\n        # - The fourth layer represents the amount of kore\n        #   that each fleet is carrying.\n        #\n        self.observation_space = spaces.Box(\n            low=-1, high=1, shape=(map_size, map_size, 4), dtype=np.float64\n        )\n\n        # Our action space will be an array of shape (2,).\n        #\n        # - The following combinations will map to the\n        #   respective actions:\n        #\n        #   - [0, 0] -> Do nothing\n        #   - [0, 1] -> Build a new ship\n        #   - [1, 0] -> Launch a fleet with a simple flight\n        #     plan (e.g. N, E, S, W). All ships will be\n        #     used to launch the fleet.\n        #   - [1, 1] -> Same as [1, 0], but only half of the\n        #     ships will be used to launch the fleet.\n        #\n        self.action_space = spaces.Box(low=0, high=1, shape=(2,), dtype=np.float64)\n\n    def map_value(self, value: Union[int, float], enemy: bool = False,) -> float:\n        \"\"\"\n        Helper function for build_observation. For this to work, we must\n        assume the following:\n            - The maximum value of Kore in a single square is expected to be\n                500.\n            - The maximum fleet size is expected to be 1000.\n            - The maximum amount of kore a fleet can carry is expected to be\n                5000.\n        Maps a value to a range of [-1, 1] if enemy, or [0, 1] otherwise.\n        \"\"\"\n        MAX_NATURAL_KORE_IN_SQUARE = 500\n        MAX_ASSUMED_FLEET_SIZE = 1000\n        MAX_ASSUMED_KORE_IN_FLEET = 5000\n        max_value = float(\n            max(\n                MAX_NATURAL_KORE_IN_SQUARE,\n                MAX_ASSUMED_FLEET_SIZE,\n                MAX_ASSUMED_KORE_IN_FLEET,\n            )\n        )\n        val = value / max_value\n        if enemy:\n            return -val\n        return val\n\n    def build_observation(self, raw_observation: Observation) -> np.ndarray:\n        \"\"\"\n        Our observation space will be a matrix of size\n        (map_size, map_size, 4).\n        \n        - The first layer is the kore count, which has\n            its values mapped to [0, 1]\n        \n        - The second layer is the fleet size, which has\n            its values mapped to [-1, 1] (negative values\n            are used to represent enemy fleets)\n        \n        - The third layer represents possible places that\n            the enemy fleets can be at the next turn. All\n            values are either -1 (enemy can be there) or\n            0 (enemy can't be there).\n        \n        - The fourth layer represents the amount of kore\n            that each fleet is carrying.\n        \"\"\"\n        # Build the Board object that will help us build the layers\n        board = Board(raw_observation, self.env_configuration)\n\n        # Building the kore layer\n        kore_layer = np.array(raw_observation.kore).reshape(\n            self.env_configuration.size, self.env_configuration.size\n        )\n\n        # Building the fleet layer\n        fleet_layer = np.zeros(\n            (self.env_configuration.size, self.env_configuration.size)\n        )\n        # - Get fleets and shipyards on the map\n        fleets = [fleet for _, fleet in board.fleets.items()]\n        shipyards = [shipyard for _, shipyard in board.shipyards.items()]\n        # - Iterate over fleets, getting its position and size\n        for fleet in fleets:\n            # - Get the position of the fleet\n            position = fleet.position\n            x, y = position.x, position.y\n            # - Get the size of the fleet\n            size = fleet.ship_count\n            # - Check if the fleet is an enemy fleet\n            if fleet.player != board.current_player:\n                multilpier = -1\n            else:\n                multilpier = 1\n            # - Set the fleet layer to the size of the fleet\n            fleet_layer[x, y] = multilpier * self.map_value(size)\n        # - Iterate over shipyards, getting its position and size\n        for shipyard in shipyards:\n            # - Get the position of the shipyard\n            position = shipyard.position\n            x, y = position.x, position.y\n            # - Get the size of the shipyard\n            size = shipyard.ship_count\n            # - Check if the shipyard is an enemy shipyard\n            if shipyard.player != board.current_player:\n                multilpier = -1\n            else:\n                multilpier = 1\n            # - Set the fleet layer to the size of the shipyard\n            fleet_layer[x, y] = multilpier * self.map_value(size)\n\n        # Building the enemy positions layer\n        enemy_positions_layer = np.zeros(\n            (self.env_configuration.size, self.env_configuration.size)\n        )\n        # - Iterate over fleets\n        for fleet in fleets:\n            # If fleet is ours, skip it\n            if fleet.player == board.current_player:\n                continue\n            # - Get the position of the fleet\n            position = fleet.position\n            x, y = position.x, position.y\n            # - Set the enemy positions layer to -1\n            enemy_positions_layer[x, y] = -1\n            enemy_positions_layer[x - 1, y] = -1\n            if x + 1 >= self.env_configuration.size:\n                enemy_positions_layer[0, y]\n            else:\n                enemy_positions_layer[x + 1, y] = -1\n            enemy_positions_layer[x, y - 1] = -1\n            if y + 1 >= self.env_configuration.size:\n                enemy_positions_layer[x, 0]\n            else:\n                enemy_positions_layer[x, y + 1] = -1\n\n        # Building the kore layer\n        kore_layer = np.zeros(\n            (self.env_configuration.size, self.env_configuration.size)\n        )\n        # - Iterate over fleets\n        for fleet in fleets:\n            # - Get the position of the fleet\n            position = fleet.position\n            x, y = position.x, position.y\n            # - Get the amount of kore the fleet is carrying\n            kore = fleet.kore\n            # - Set the kore layer to the amount of kore\n            kore_layer[x, y] = kore\n\n        # Building our observation box\n        observation = np.zeros(\n            (self.env_configuration.size, self.env_configuration.size, 4)\n        )\n        observation[:, :, 0] = kore_layer\n        observation[:, :, 1] = fleet_layer\n        observation[:, :, 2] = enemy_positions_layer\n        observation[:, :, 3] = kore_layer\n\n        return observation\n\n    def map_reward(\n        self, old_reward: Union[int, float],\n    ):\n        \"\"\"\n        If you want to modify the reward, you can do it here.\n        \"\"\"\n        return old_reward\n\n    # def get_my_shipyard(self) -> Shipyard:\n    #     \"\"\"\n    #     Returns the shipyard of the current player.\n    #     \"\"\"\n    #     for shipyard in self.board.current_player.sh\n\n    def match_action(self, action_space: np.ndarray) -> ShipyardAction:\n        \"\"\"\n        This function will match the action space to a\n        ShipyardAction.\n        \"\"\"\n        # If there are no shipyards, return None\n        if len(self.board.current_player.shipyards) == 0:\n            return None\n        action_space = np.round_(action_space[0], decimals=0).astype(int)\n        # - Check if the action space is [0, 0]\n        if action_space[0] == 0 and action_space[1] == 0:\n            return None\n        # - Check if the action space is [0, 1]\n        elif action_space[0] == 0 and action_space[1] == 1:\n            return ShipyardAction.spawn_ships(1)\n        # - Check if the action space is [1, 0]\n        elif action_space[0] == 1 and action_space[1] == 0:\n            ships_in_fleet = self.board.current_player.shipyards[0].ship_count\n            if ships_in_fleet == 0:\n                return None\n            return ShipyardAction.launch_fleet_with_flight_plan(\n                self.board.current_player.shipyards[0].ship_count,\n                choice([\"N\", \"E\", \"S\", \"W\"])\n            )\n        # - Check if the action space is [1, 1]\n        elif action_space[0] == 1 and action_space[1] == 1:\n            ships_in_fleet = int(self.board.current_player.shipyards[0].ship_count / 2)\n            if ships_in_fleet == 0 and self.board.current_player.shipyards[0].ship_count > 0:\n                ships_in_fleet = 1\n            else:\n                return None\n            return ShipyardAction.launch_fleet_with_flight_plan(\n                ships_in_fleet,\n                choice([\"N\", \"E\", \"S\", \"W\"])\n            )\n        else:\n            raise ValueError(f\"Invalid action space: {action_space}\")\n\n    def reset(self):\n        \"\"\"\n        Resets the environment.\n        \"\"\"\n        self.raw_observation = self.env.reset()\n        obs = self.build_observation(self.raw_observation)\n        return obs\n\n    def step(self, action_space: np.ndarray):\n        \"\"\"\n        Performs an action in the environment.\n        \"\"\"\n        # Get the Board object and update it\n        self.board = Board(self.raw_observation, self.env_configuration)\n        # Sets done if no shipyards are left\n        if len(self.board.current_player.shipyards) == 0:\n            return np.zeros((21, 21, 4)), 0, True, {}\n        # Get the action for the shipyard\n        action = self.match_action(action_space)\n        self.board.current_player.shipyards[0].next_action = action\n        self.raw_observation, old_reward, done, info = self.env.step(\n            self.board.current_player.next_actions\n        )\n        observation = self.build_observation(self.raw_observation)\n        reward = self.map_reward(old_reward)\n        return observation, reward, done, info\n    \n    \ndef build_env():\n    return CustomKoreEnv()\n\ndef build_model():\n    return models.Sequential(\n        [\n            layers.Input(shape=(21, 21, 4)),\n            layers.Conv2D(64, 8),\n            layers.Activation(\"linear\"),\n            layers.Conv2D(128, 10),\n            layers.Activation(\"linear\"),\n            layers.Flatten(),\n            layers.Dense(32),\n            layers.Activation(\"linear\"),\n            layers.Dense(2),\n            layers.Activation(\"sigmoid\")\n        ]\n    )\n","metadata":{"execution":{"iopub.status.busy":"2022-07-02T12:19:08.221648Z","iopub.execute_input":"2022-07-02T12:19:08.222929Z","iopub.status.idle":"2022-07-02T12:19:08.24538Z","shell.execute_reply.started":"2022-07-02T12:19:08.222881Z","shell.execute_reply":"2022-07-02T12:19:08.244495Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile main.py\n# All this syspath wranglig is needed to make sure that the agent runs on the target environment and can load both the external dependencies\n# and the saved model. Dear kaggle, if possible, please make this easier!\nimport os\nimport sys\nKAGGLE_AGENT_PATH = \"/kaggle_simulations/agent/\"\nif os.path.exists(KAGGLE_AGENT_PATH):\n    # We're in the kaggle target system\n    sys.path.insert(0, os.path.join(KAGGLE_AGENT_PATH, 'lib'))\n    agent_path = os.path.join(KAGGLE_AGENT_PATH, 'baseline_agent')\nelse:\n    # We're somewhere else\n    sys.path.insert(0, os.path.join(os.getcwd(), 'lib'))\n    agent_path = './'\n\n\ncustom_env = CustomKoreEnv()\nmodel: models.Sequential = models.load_model(\"./model\")\n\ndef agent(obs: Observation, config: Configuration):\n    model_observation = np.reshape(custom_env.build_observation(obs), [1, 21, 21, 4])\n    action_space = model.predict(model_observation)\n    # Iterate over shipyards\n    board = Board(obs, config)\n    custom_env.board = board\n    if board.current_player.shipyards:\n        board.current_player.shipyards[0].next_action = custom_env.match_action(action_space)\n    return board.current_player.next_actions","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-07-03T11:53:37.472405Z","iopub.execute_input":"2022-07-03T11:53:37.472792Z","iopub.status.idle":"2022-07-03T11:53:37.481237Z","shell.execute_reply.started":"2022-07-03T11:53:37.472753Z","shell.execute_reply":"2022-07-03T11:53:37.480079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!tar -czf submission.tar.gz main.py environment.py model.zip lib","metadata":{},"execution_count":null,"outputs":[]}]}