├── notebooks └── utils │ ├── __init__.py │ ├── football_tools.py │ └── gif_tools.py ├── .vscode └── settings.json ├── docker ├── start-notebook.sh ├── fix-permissions ├── 1_vs_1.py ├── 2_vs_2.py ├── 2_vs_2_auto_GK.py ├── start-singleuser.sh ├── 3_vs_3.py ├── 3_vs_3_auto_GK.py ├── 4_vs_4.py ├── jupyter_notebook_config.py ├── 4_vs_4_auto_GK.py ├── 5_vs_5.py ├── 5_vs_5_auto_GK.py ├── policy.xml ├── start.sh └── Dockerfile ├── LICENSE ├── README.md └── .gitignore /notebooks/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": "/usr/bin/python3" 3 | } -------------------------------------------------------------------------------- /docker/start-notebook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Jupyter Development Team. 3 | # Distributed under the terms of the Modified BSD License. 4 | 5 | set -e 6 | 7 | if [[ ! -z "${JUPYTERHUB_API_TOKEN}" ]]; then 8 | # launched by JupyterHub, use single-user entrypoint 9 | exec /usr/local/bin/start-singleuser.sh $* 10 | else 11 | if [[ ! -z "${JUPYTER_ENABLE_LAB}" ]]; then 12 | . /usr/local/bin/start.sh jupyter lab $* 13 | else 14 | . /usr/local/bin/start.sh jupyter notebook $* 15 | fi 16 | fi 17 | -------------------------------------------------------------------------------- /docker/fix-permissions: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # set permissions on a directory 3 | # after any installation, if a directory needs to be (human) user-writable, 4 | # run this script on it. 5 | # It will make everything in the directory owned by the group $NB_GID 6 | # and writable by that group. 7 | # Deployments that want to set a specific user id can preserve permissions 8 | # by adding the `--group-add users` line to `docker run`. 9 | 10 | # uses find to avoid touching files that already have the right permissions, 11 | # which would cause massive image explosion 12 | 13 | # right permissions are: 14 | # group=$NB_GID 15 | # AND permissions include group rwX (directory-execute) 16 | # AND directories have setuid,setgid bits set 17 | 18 | set -e 19 | 20 | for d in $@; do 21 | find "$d" \ 22 | ! \( \ 23 | -group $NB_GID \ 24 | -a -perm -g+rwX \ 25 | \) \ 26 | -exec chgrp $NB_GID {} \; \ 27 | -exec chmod g+rwX {} \; 28 | # setuid,setgid *on directories only* 29 | find "$d" \ 30 | \( \ 31 | -type d \ 32 | -a ! -perm -6000 \ 33 | \) \ 34 | -exec chmod +6000 {} \; 35 | done 36 | -------------------------------------------------------------------------------- /docker/1_vs_1.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright 2019 Google LLC 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | 17 | 18 | 19 | 20 | from . import * 21 | 22 | def build_scenario(builder): 23 | builder.config().game_duration = 1000 24 | builder.config().right_team_difficulty = 0.05 25 | builder.config().left_team_difficulty = 0.05 26 | builder.config().deterministic = False 27 | if builder.EpisodeNumber() % 2 == 0: 28 | first_team = Team.e_Left 29 | second_team = Team.e_Right 30 | else: 31 | first_team = Team.e_Right 32 | second_team = Team.e_Left 33 | builder.SetTeam(first_team) 34 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 35 | builder.SetTeam(second_team) 36 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 37 | -------------------------------------------------------------------------------- /docker/2_vs_2.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright 2019 Google LLC 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | 17 | 18 | 19 | 20 | from . import * 21 | 22 | def build_scenario(builder): 23 | builder.config().game_duration = 1000 24 | builder.config().right_team_difficulty = 0.05 25 | builder.config().left_team_difficulty = 0.05 26 | builder.config().deterministic = False 27 | if builder.EpisodeNumber() % 2 == 0: 28 | first_team = Team.e_Left 29 | second_team = Team.e_Right 30 | else: 31 | first_team = Team.e_Right 32 | second_team = Team.e_Left 33 | builder.SetTeam(first_team) 34 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 35 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 36 | builder.SetTeam(second_team) 37 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 38 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 39 | -------------------------------------------------------------------------------- /docker/2_vs_2_auto_GK.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright 2019 Google LLC 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | 17 | 18 | 19 | 20 | from . import * 21 | 22 | def build_scenario(builder): 23 | builder.config().game_duration = 1000 24 | builder.config().right_team_difficulty = 0.05 25 | builder.config().left_team_difficulty = 0.05 26 | builder.config().deterministic = False 27 | if builder.EpisodeNumber() % 2 == 0: 28 | first_team = Team.e_Left 29 | second_team = Team.e_Right 30 | else: 31 | first_team = Team.e_Right 32 | second_team = Team.e_Left 33 | builder.SetTeam(first_team) 34 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK, controllable=False) 35 | builder.AddPlayer( 0.0, -0.1, e_PlayerRole_CF) 36 | builder.SetTeam(second_team) 37 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK, controllable=False) 38 | builder.AddPlayer( 0.0, -0.1, e_PlayerRole_CF) 39 | -------------------------------------------------------------------------------- /docker/start-singleuser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Jupyter Development Team. 3 | # Distributed under the terms of the Modified BSD License. 4 | 5 | set -e 6 | 7 | # set default ip to 0.0.0.0 8 | if [[ "$NOTEBOOK_ARGS $@" != *"--ip="* ]]; then 9 | NOTEBOOK_ARGS="--ip=0.0.0.0 $NOTEBOOK_ARGS" 10 | fi 11 | 12 | # handle some deprecated environment variables 13 | # from DockerSpawner < 0.8. 14 | # These won't be passed from DockerSpawner 0.9, 15 | # so avoid specifying --arg=empty-string 16 | if [ ! -z "$NOTEBOOK_DIR" ]; then 17 | NOTEBOOK_ARGS="--notebook-dir='$NOTEBOOK_DIR' $NOTEBOOK_ARGS" 18 | fi 19 | if [ ! -z "$JPY_PORT" ]; then 20 | NOTEBOOK_ARGS="--port=$JPY_PORT $NOTEBOOK_ARGS" 21 | fi 22 | if [ ! -z "$JPY_USER" ]; then 23 | NOTEBOOK_ARGS="--user=$JPY_USER $NOTEBOOK_ARGS" 24 | fi 25 | if [ ! -z "$JPY_COOKIE_NAME" ]; then 26 | NOTEBOOK_ARGS="--cookie-name=$JPY_COOKIE_NAME $NOTEBOOK_ARGS" 27 | fi 28 | if [ ! -z "$JPY_BASE_URL" ]; then 29 | NOTEBOOK_ARGS="--base-url=$JPY_BASE_URL $NOTEBOOK_ARGS" 30 | fi 31 | if [ ! -z "$JPY_HUB_PREFIX" ]; then 32 | NOTEBOOK_ARGS="--hub-prefix=$JPY_HUB_PREFIX $NOTEBOOK_ARGS" 33 | fi 34 | if [ ! -z "$JPY_HUB_API_URL" ]; then 35 | NOTEBOOK_ARGS="--hub-api-url=$JPY_HUB_API_URL $NOTEBOOK_ARGS" 36 | fi 37 | if [ ! -z "$JUPYTER_ENABLE_LAB" ]; then 38 | NOTEBOOK_BIN="jupyter labhub" 39 | else 40 | NOTEBOOK_BIN=jupyterhub-singleuser 41 | fi 42 | 43 | . /usr/local/bin/start.sh $NOTEBOOK_BIN $NOTEBOOK_ARGS $@ 44 | -------------------------------------------------------------------------------- /docker/3_vs_3.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright 2019 Google LLC 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | 17 | 18 | 19 | 20 | from . import * 21 | 22 | def build_scenario(builder): 23 | builder.config().game_duration = 1000 24 | builder.config().right_team_difficulty = 0.05 25 | builder.config().left_team_difficulty = 0.05 26 | builder.config().deterministic = False 27 | if builder.EpisodeNumber() % 2 == 0: 28 | first_team = Team.e_Left 29 | second_team = Team.e_Right 30 | else: 31 | first_team = Team.e_Right 32 | second_team = Team.e_Left 33 | builder.SetTeam(first_team) 34 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 35 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 36 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 37 | builder.SetTeam(second_team) 38 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 39 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 40 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 41 | -------------------------------------------------------------------------------- /docker/3_vs_3_auto_GK.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright 2019 Google LLC 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | 17 | 18 | 19 | 20 | from . import * 21 | 22 | def build_scenario(builder): 23 | builder.config().game_duration = 1000 24 | builder.config().right_team_difficulty = 0.05 25 | builder.config().left_team_difficulty = 0.05 26 | builder.config().deterministic = False 27 | if builder.EpisodeNumber() % 2 == 0: 28 | first_team = Team.e_Left 29 | second_team = Team.e_Right 30 | else: 31 | first_team = Team.e_Right 32 | second_team = Team.e_Left 33 | builder.SetTeam(first_team) 34 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK, controllable=False) 35 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 36 | builder.AddPlayer( 0.0, -0.1, e_PlayerRole_CF) 37 | builder.SetTeam(second_team) 38 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK, controllable=False) 39 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 40 | builder.AddPlayer( 0.0, -0.1, e_PlayerRole_CF) 41 | -------------------------------------------------------------------------------- /docker/4_vs_4.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright 2019 Google LLC 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | 17 | 18 | 19 | 20 | from . import * 21 | 22 | def build_scenario(builder): 23 | builder.config().game_duration = 1000 24 | builder.config().right_team_difficulty = 0.05 25 | builder.config().left_team_difficulty = 0.05 26 | builder.config().deterministic = False 27 | if builder.EpisodeNumber() % 2 == 0: 28 | first_team = Team.e_Left 29 | second_team = Team.e_Right 30 | else: 31 | first_team = Team.e_Right 32 | second_team = Team.e_Left 33 | builder.SetTeam(first_team) 34 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 35 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 36 | builder.AddPlayer( 0.3, 0.1, e_PlayerRole_DM) 37 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 38 | builder.SetTeam(second_team) 39 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 40 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 41 | builder.AddPlayer( 0.3, 0.1, e_PlayerRole_DM) 42 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Miguel Morales 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /docker/jupyter_notebook_config.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jupyter Development Team. 2 | # Distributed under the terms of the Modified BSD License. 3 | 4 | from jupyter_core.paths import jupyter_data_dir 5 | import subprocess 6 | import os 7 | import errno 8 | import stat 9 | 10 | c = get_config() 11 | c.NotebookApp.allow_origin = '*' 12 | c.NotebookApp.ip = '0.0.0.0' 13 | c.NotebookApp.port = 8888 14 | c.NotebookApp.open_browser = False 15 | c.NotebookApp.password = u'sha1:2446d8d21de0:3774ae0a0b0b5f01eed10552facb041afe8ca481' 16 | 17 | # https://github.com/jupyter/notebook/issues/3130 18 | c.FileContentsManager.delete_to_trash = False 19 | 20 | # Generate a self-signed certificate 21 | if 'GEN_CERT' in os.environ: 22 | dir_name = jupyter_data_dir() 23 | pem_file = os.path.join(dir_name, 'notebook.pem') 24 | try: 25 | os.makedirs(dir_name) 26 | except OSError as exc: # Python >2.5 27 | if exc.errno == errno.EEXIST and os.path.isdir(dir_name): 28 | pass 29 | else: 30 | raise 31 | # Generate a certificate if one doesn't exist on disk 32 | subprocess.check_call(['openssl', 'req', '-new', 33 | '-newkey', 'rsa:2048', 34 | '-days', '365', 35 | '-nodes', '-x509', 36 | '-subj', '/C=XX/ST=XX/L=XX/O=generated/CN=generated', 37 | '-keyout', pem_file, 38 | '-out', pem_file]) 39 | # Restrict access to the file 40 | os.chmod(pem_file, stat.S_IRUSR | stat.S_IWUSR) 41 | c.NotebookApp.certfile = pem_file 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reinforcement Learning and Decision Making 2 | 3 | **Note:** At the moment, only running the code from the [docker](https://github.com/docker/docker-ce) container (below) is supported. Docker allows for creating a single environment that is more likely to work on all systems. Basically, I install and configure all packages for you, except docker itself, and you just run the code on a tested environment. 4 | 5 | To install docker, I recommend a web search for "installing docker on \". For running the code on a GPU, you have to additionally install [nvidia-docker](https://github.com/NVIDIA/nvidia-docker). NVIDIA Docker allows for using a host's GPUs inside docker containers. After you have docker (and nvidia-docker if using a GPU) installed, follow the three steps below. 6 | 7 | ## Running the code 8 | 0. Clone this repo: 9 | `git clone --depth 1 https://www.github.com/mimoralea/gfootball-intro.git && cd gfootball-intro` 10 | 1. Pull the rldm image with: 11 | `docker pull mimoralea/rldm:v0.4` 12 | 2. Spin up a container: 13 | - On Mac or Linux: 14 | `docker run -it --rm --shm-size="10GB" -p 8888:8888 -p 6006:6006 -p 8265:8265 -v "$PWD"/notebooks/:/mnt/notebooks/ mimoralea/rldm:v0.4` 15 | - On Windows: 16 | `docker run -it --rm --shm-size="10GB" -p 8888:8888 -p 6006:6006 -p 8265:8265 -v %CD%/notebooks/:/mnt/notebooks/ mimoralea/rldm:v0.4` 17 | - NOTE: Use `nvidia-docker` if you are using a GPU. 18 | 3. Open a browser and go to the URL shown in the terminal (likely to be: http://localhost:8888). The password is: `rldm`, Tensorboard is http://localhost:6006, Ray Dashboard is http://localhost:8265. 19 | -------------------------------------------------------------------------------- /docker/4_vs_4_auto_GK.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright 2019 Google LLC 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | 17 | 18 | 19 | 20 | from . import * 21 | 22 | def build_scenario(builder): 23 | builder.config().game_duration = 1000 24 | builder.config().right_team_difficulty = 0.05 25 | builder.config().left_team_difficulty = 0.05 26 | builder.config().deterministic = False 27 | if builder.EpisodeNumber() % 2 == 0: 28 | first_team = Team.e_Left 29 | second_team = Team.e_Right 30 | else: 31 | first_team = Team.e_Right 32 | second_team = Team.e_Left 33 | builder.SetTeam(first_team) 34 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK, controllable=False) 35 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 36 | builder.AddPlayer( 0.3, 0.2, e_PlayerRole_DM) 37 | builder.AddPlayer( 0.0, -0.1, e_PlayerRole_CF) 38 | builder.SetTeam(second_team) 39 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK, controllable=False) 40 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 41 | builder.AddPlayer( 0.3, 0.2, e_PlayerRole_DM) 42 | builder.AddPlayer( 0.0, -0.1, e_PlayerRole_CF) 43 | -------------------------------------------------------------------------------- /docker/5_vs_5.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright 2019 Google LLC 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | 17 | 18 | 19 | 20 | from . import * 21 | 22 | def build_scenario(builder): 23 | builder.config().game_duration = 1000 24 | builder.config().right_team_difficulty = 0.05 25 | builder.config().left_team_difficulty = 0.05 26 | builder.config().deterministic = False 27 | if builder.EpisodeNumber() % 2 == 0: 28 | first_team = Team.e_Left 29 | second_team = Team.e_Right 30 | else: 31 | first_team = Team.e_Right 32 | second_team = Team.e_Left 33 | builder.SetTeam(first_team) 34 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 35 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 36 | builder.AddPlayer( 0.3, 0.1, e_PlayerRole_DM) 37 | builder.AddPlayer( 0.5, -0.1, e_PlayerRole_AM) 38 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 39 | builder.SetTeam(second_team) 40 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK) 41 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 42 | builder.AddPlayer( 0.3, 0.1, e_PlayerRole_DM) 43 | builder.AddPlayer( 0.5, -0.1, e_PlayerRole_AM) 44 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | .idea/ 103 | -------------------------------------------------------------------------------- /docker/5_vs_5_auto_GK.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright 2019 Google LLC 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | 17 | 18 | 19 | 20 | from . import * 21 | 22 | def build_scenario(builder): 23 | builder.config().game_duration = 1000 24 | builder.config().right_team_difficulty = 0.05 25 | builder.config().left_team_difficulty = 0.05 26 | builder.config().deterministic = False 27 | if builder.EpisodeNumber() % 2 == 0: 28 | first_team = Team.e_Left 29 | second_team = Team.e_Right 30 | else: 31 | first_team = Team.e_Right 32 | second_team = Team.e_Left 33 | builder.SetTeam(first_team) 34 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK, controllable=False) 35 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 36 | builder.AddPlayer( 0.3, 0.1, e_PlayerRole_DM) 37 | builder.AddPlayer( 0.5, -0.1, e_PlayerRole_AM) 38 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 39 | builder.SetTeam(second_team) 40 | builder.AddPlayer(-1.0, 0.0, e_PlayerRole_GK, controllable=False) 41 | builder.AddPlayer(-0.5, 0.0, e_PlayerRole_CB) 42 | builder.AddPlayer( 0.3, 0.1, e_PlayerRole_DM) 43 | builder.AddPlayer( 0.5, -0.1, e_PlayerRole_AM) 44 | builder.AddPlayer( 0.9, 0.0, e_PlayerRole_CF) 45 | -------------------------------------------------------------------------------- /docker/policy.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ]> 11 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /notebooks/utils/football_tools.py: -------------------------------------------------------------------------------- 1 | import gym 2 | import numpy as np 3 | from gfootball import env as fe 4 | 5 | from ray.rllib.env.multi_agent_env import MultiAgentEnv 6 | 7 | 8 | def create_football_env(n_vs_n=1, auto_GK=True, logdir="/tmp/football", add_checkpoint=True, dump_frequency=1): 9 | """ 10 | Returns a custom gfootball environment 11 | 12 | n_vs_n=1 # 1...5 number of players 13 | auto_GK=True # let the computer control the GK 14 | """ 15 | try: 16 | del env 17 | except: 18 | pass 19 | 20 | n_control = max(1, n_vs_n - int(auto_GK)) 21 | auto_GK="_auto_GK" if auto_GK and n_vs_n > 1 else "" # auto_GK only when more than 1 player 22 | reward_type='checkpoint,scoring' if add_checkpoint else 'scoring' 23 | env = fe.create_environment( 24 | # env_name="academy_empty_goal_close", 25 | env_name=f"{n_vs_n}_vs_{n_vs_n}{auto_GK}", 26 | stacked=False, 27 | representation='simple115v2', 28 | # scoring is 1 for scoring a goal, -1 the opponent scoring a goal 29 | # checkpoint is +0.1 first time player gets to an area (10 checkpoint total, +1 reward max) 30 | rewards=reward_type, 31 | logdir=logdir, 32 | write_goal_dumps=True, 33 | write_full_episode_dumps=True, 34 | render=False, 35 | write_video=True, 36 | dump_frequency=dump_frequency, 37 | extra_players=None, 38 | number_of_left_players_agent_controls=n_control, 39 | number_of_right_players_agent_controls=0) 40 | return env 41 | 42 | 43 | class RllibGFootball(MultiAgentEnv): 44 | def __init__(self, n_vs_n=1, auto_GK=True, logdir="/tmp/football", add_checkpoint=True, dump_frequency=1): 45 | self.env = create_football_env(n_vs_n=n_vs_n, 46 | auto_GK=auto_GK, 47 | logdir=logdir, 48 | add_checkpoint=add_checkpoint, 49 | dump_frequency=dump_frequency) 50 | 51 | num_agents = max(1, n_vs_n - int(auto_GK)) 52 | self.action_space = self.env.action_space if \ 53 | num_agents else gym.spaces.Discrete(self.env.action_space.nvec[1]) 54 | self.observation_space = self.env.observation_space if \ 55 | num_agents else gym.spaces.Box( 56 | low=self.env.observation_space.low[0], 57 | high=self.env.observation_space.high[0], 58 | dtype=self.env.observation_space.dtype) 59 | self.num_agents = num_agents 60 | self.reward_range = np.array((-np.inf, np.inf)) 61 | self.metadata = {'render.modes': ['human', 'rgb_array'], 'video.frames_per_second': 50} 62 | self.spec = None 63 | 64 | def reset(self): 65 | original_obs = self.env.reset() 66 | obs = {} 67 | for x in range(self.num_agents): 68 | if self.num_agents > 1: 69 | obs['agent_%d' % x] = original_obs[x] 70 | else: 71 | obs['agent_%d' % x] = original_obs 72 | return obs 73 | 74 | def step(self, action_dict): 75 | actions = [] 76 | for key, value in sorted(action_dict.items()): 77 | actions.append(value) 78 | o, r, d, i = self.env.step(actions) 79 | obs = {} 80 | rewards = {} 81 | dones = {} 82 | infos = {} 83 | for pos, key in enumerate(sorted(action_dict.keys())): 84 | infos[key] = i 85 | if self.num_agents > 1: 86 | rewards[key] = r[pos] 87 | obs[key] = o[pos] 88 | dones[key] = d 89 | else: 90 | rewards[key] = r 91 | obs[key] = o 92 | dones[key] = d 93 | dones['__all__'] = d 94 | return obs, rewards, dones, infos -------------------------------------------------------------------------------- /notebooks/utils/gif_tools.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | 4 | import subprocess 5 | import base64 6 | import glob 7 | import io 8 | import json 9 | 10 | from gfootball.env.script_helpers import ScriptHelpers as sh 11 | 12 | 13 | def get_gif_html(videos_path, title, subtitle_eps=None, max_n_videos=4): 14 | videos = np.array(glob.glob(videos_path)) 15 | dumps = np.array(glob.glob(videos_path)) 16 | if len(videos) == 0: 17 | return 18 | 19 | n_videos = max(1, min(max_n_videos, len(videos))) 20 | idxs = np.linspace(0, len(videos) - 1, n_videos).astype(int) if n_videos > 1 else [-1,] 21 | videos = videos[idxs,...] 22 | vids = {} 23 | for video_path in videos: 24 | basename = os.path.splitext(video_path)[0] 25 | gif_path = basename + '.gif' 26 | dump_path = basename + '.dump' 27 | 28 | if not os.path.exists(gif_path): 29 | ps = subprocess.Popen( 30 | ('ffmpeg', 31 | '-i', video_path, 32 | '-r', '10', 33 | '-f', 'image2pipe', 34 | '-vcodec', 'ppm', 35 | '-crf', '20', 36 | '-vf', 'scale=300:-1', 37 | '-'), 38 | stdout=subprocess.PIPE) 39 | output = subprocess.check_output( 40 | ('convert', 41 | '-coalesce', 42 | '-delay', '7', 43 | '-loop', '0', 44 | '-fuzz', '2%', 45 | '+dither', 46 | '-deconstruct', 47 | '-layers', 'Optimize', 48 | '-', gif_path), 49 | stdin=ps.stdout) 50 | ps.wait() 51 | 52 | gif = io.open(gif_path, 'r+b').read() 53 | encoded = base64.b64encode(gif) 54 | 55 | 56 | html_tag = """ 57 |

{0}

58 | """ 59 | prefix = 'Trial ' if subtitle_eps is None else 'Episode ' 60 | d = sh().load_dump(dump_path) 61 | episode_id = d[0]['debug']['config']['episode_number'] 62 | sufix = str(episode_id if subtitle_eps is None \ 63 | else subtitle_eps[episode_id]) 64 | vids[episode_id] = html_tag.format(prefix + sufix, encoded.decode('ascii')) 65 | 66 | strm = '

{}

'.format(title) 67 | for _, v in sorted(vids.items()): 68 | strm += v 69 | return strm 70 | 71 | def get_gif_html_oai(env_videos, title, subtitle_eps=None, max_n_videos=4): 72 | videos = np.array(env_videos) 73 | if len(videos) == 0: 74 | return 75 | 76 | n_videos = max(1, min(max_n_videos, len(videos))) 77 | idxs = np.linspace(0, len(videos) - 1, n_videos).astype(int) if n_videos > 1 else [-1,] 78 | videos = videos[idxs,...] 79 | 80 | strm = '

{}

'.format(title) 81 | for video_path, meta_path in videos: 82 | basename = os.path.splitext(video_path)[0] 83 | gif_path = basename + '.gif' 84 | if not os.path.exists(gif_path): 85 | ps = subprocess.Popen( 86 | ('ffmpeg', 87 | '-i', video_path, 88 | '-r', '7', 89 | '-f', 'image2pipe', 90 | '-vcodec', 'ppm', 91 | '-crf', '20', 92 | '-vf', 'scale=512:-1', 93 | '-'), 94 | stdout=subprocess.PIPE) 95 | output = subprocess.check_output( 96 | ('convert', 97 | '-coalesce', 98 | '-delay', '7', 99 | '-loop', '0', 100 | '-fuzz', '2%', 101 | '+dither', 102 | '-deconstruct', 103 | '-layers', 'Optimize', 104 | '-', gif_path), 105 | stdin=ps.stdout) 106 | ps.wait() 107 | 108 | gif = io.open(gif_path, 'r+b').read() 109 | encoded = base64.b64encode(gif) 110 | 111 | with open(meta_path) as data_file: 112 | meta = json.load(data_file) 113 | 114 | html_tag = """ 115 |

{0}

116 | """ 117 | prefix = 'Trial ' if subtitle_eps is None else 'Episode ' 118 | sufix = str(meta['episode_id'] if subtitle_eps is None \ 119 | else subtitle_eps[meta['episode_id']]) 120 | strm += html_tag.format(prefix + sufix, encoded.decode('ascii')) 121 | return strm 122 | -------------------------------------------------------------------------------- /docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Jupyter Development Team. 3 | # Distributed under the terms of the Modified BSD License. 4 | 5 | set -e 6 | 7 | # Exec the specified command or fall back on bash 8 | if [ $# -eq 0 ]; then 9 | cmd=bash 10 | else 11 | cmd=$* 12 | fi 13 | 14 | run-hooks () { 15 | # Source scripts or run executable files in a directory 16 | if [[ ! -d "$1" ]] ; then 17 | return 18 | fi 19 | echo "$0: running hooks in $1" 20 | for f in "$1"/*; do 21 | case "$f" in 22 | *.sh) 23 | echo "$0: running $f" 24 | source "$f" 25 | ;; 26 | *) 27 | if [[ -x "$f" ]] ; then 28 | echo "$0: running $f" 29 | "$f" 30 | else 31 | echo "$0: ignoring $f" 32 | fi 33 | ;; 34 | esac 35 | echo "$0: done running hooks in $1" 36 | done 37 | } 38 | 39 | run-hooks /usr/local/bin/start-notebook.d 40 | 41 | # Handle special flags if we're root 42 | if [ $(id -u) == 0 ] ; then 43 | 44 | # Only attempt to change the jovyan username if it exists 45 | if id jovyan &> /dev/null ; then 46 | echo "Set username to: $NB_USER" 47 | usermod -d /home/$NB_USER -l $NB_USER jovyan 48 | fi 49 | 50 | # Handle case where provisioned storage does not have the correct permissions by default 51 | # Ex: default NFS/EFS (no auto-uid/gid) 52 | if [[ "$CHOWN_HOME" == "1" || "$CHOWN_HOME" == 'yes' ]]; then 53 | echo "Changing ownership of /home/$NB_USER to $NB_UID:$NB_GID" 54 | chown $CHOWN_HOME_OPTS $NB_UID:$NB_GID /home/$NB_USER 55 | fi 56 | if [ ! -z "$CHOWN_EXTRA" ]; then 57 | for extra_dir in $(echo $CHOWN_EXTRA | tr ',' ' '); do 58 | chown $CHOWN_EXTRA_OPTS $NB_UID:$NB_GID $extra_dir 59 | done 60 | fi 61 | 62 | # handle home and working directory if the username changed 63 | if [[ "$NB_USER" != "jovyan" ]]; then 64 | # changing username, make sure homedir exists 65 | # (it could be mounted, and we shouldn't create it if it already exists) 66 | if [[ ! -e "/home/$NB_USER" ]]; then 67 | echo "Relocating home dir to /home/$NB_USER" 68 | mv /home/jovyan "/home/$NB_USER" 69 | fi 70 | # if workdir is in /home/jovyan, cd to /home/$NB_USER 71 | if [[ "$PWD/" == "/home/jovyan/"* ]]; then 72 | newcwd="/home/$NB_USER/${PWD:13}" 73 | echo "Setting CWD to $newcwd" 74 | cd "$newcwd" 75 | fi 76 | fi 77 | 78 | # Change UID of NB_USER to NB_UID if it does not match 79 | if [ "$NB_UID" != $(id -u $NB_USER) ] ; then 80 | echo "Set $NB_USER UID to: $NB_UID" 81 | usermod -u $NB_UID $NB_USER 82 | fi 83 | 84 | # Set NB_USER primary gid to NB_GID (after making the group). Set 85 | # supplementary gids to NB_GID and 100. 86 | if [ "$NB_GID" != $(id -g $NB_USER) ] ; then 87 | echo "Add $NB_USER to group: $NB_GID" 88 | groupadd -g $NB_GID -o ${NB_GROUP:-${NB_USER}} 89 | usermod -g $NB_GID -a -G $NB_GID,100 $NB_USER 90 | fi 91 | 92 | # Enable sudo if requested 93 | if [[ "$GRANT_SUDO" == "1" || "$GRANT_SUDO" == 'yes' ]]; then 94 | echo "Granting $NB_USER sudo access and appending $CONDA_DIR/bin to sudo PATH" 95 | echo "$NB_USER ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/notebook 96 | fi 97 | 98 | # Add $CONDA_DIR/bin to sudo secure_path 99 | sed -r "s#Defaults\s+secure_path=\"([^\"]+)\"#Defaults secure_path=\"\1:$CONDA_DIR/bin\"#" /etc/sudoers | grep secure_path > /etc/sudoers.d/path 100 | 101 | # Exec the command as NB_USER with the PATH and the rest of 102 | # the environment preserved 103 | run-hooks /usr/local/bin/before-notebook.d 104 | echo "Executing the command: $cmd" 105 | exec sudo -E -H -u $NB_USER PATH=$PATH XDG_CACHE_HOME=/home/$NB_USER/.cache PYTHONPATH=$PYTHONPATH $cmd 106 | else 107 | if [[ "$NB_UID" == "$(id -u jovyan)" && "$NB_GID" == "$(id -g jovyan)" ]]; then 108 | # User is not attempting to override user/group via environment 109 | # variables, but they could still have overridden the uid/gid that 110 | # container runs as. Check that the user has an entry in the passwd 111 | # file and if not add an entry. 112 | whoami &> /dev/null || STATUS=$? && true 113 | if [[ "$STATUS" != "0" ]]; then 114 | if [[ -w /etc/passwd ]]; then 115 | echo "Adding passwd file entry for $(id -u)" 116 | cat /etc/passwd | sed -e "s/^jovyan:/nayvoj:/" > /tmp/passwd 117 | echo "jovyan:x:$(id -u):$(id -g):,,,:/home/jovyan:/bin/bash" >> /tmp/passwd 118 | cat /tmp/passwd > /etc/passwd 119 | rm /tmp/passwd 120 | else 121 | echo 'Container must be run with group "root" to update passwd file' 122 | fi 123 | fi 124 | 125 | # Warn if the user isn't going to be able to write files to $HOME. 126 | if [[ ! -w /home/jovyan ]]; then 127 | echo 'Container must be run with group "users" to update files' 128 | fi 129 | else 130 | # Warn if looks like user want to override uid/gid but hasn't 131 | # run the container as root. 132 | if [[ ! -z "$NB_UID" && "$NB_UID" != "$(id -u)" ]]; then 133 | echo 'Container must be run as root to set $NB_UID' 134 | fi 135 | if [[ ! -z "$NB_GID" && "$NB_GID" != "$(id -g)" ]]; then 136 | echo 'Container must be run as root to set $NB_GID' 137 | fi 138 | fi 139 | 140 | # Warn if looks like user want to run in sudo mode but hasn't run 141 | # the container as root. 142 | if [[ "$GRANT_SUDO" == "1" || "$GRANT_SUDO" == 'yes' ]]; then 143 | echo 'Container must be run as root to grant sudo permissions' 144 | fi 145 | 146 | # Execute the command 147 | run-hooks /usr/local/bin/before-notebook.d 148 | echo "Executing the command: $cmd" 149 | exec $cmd 150 | fi 151 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nvidia/cuda:10.2-cudnn7-devel-ubuntu18.04 2 | LABEL maintainer="Miguel Morales " 3 | 4 | ARG NB_USER="jovyan" 5 | ARG NB_UID="1000" 6 | ARG NB_GID="100" 7 | 8 | # Fix DL4006 9 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 10 | 11 | USER root 12 | 13 | # update ubuntu installation 14 | ENV DEBIAN_FRONTEND noninteractive 15 | RUN apt-get update && \ 16 | apt-get install -yq --no-install-recommends \ 17 | build-essential \ 18 | bzip2 \ 19 | ca-certificates \ 20 | cmake \ 21 | curl \ 22 | emacs \ 23 | emacs-nox \ 24 | ffmpeg \ 25 | flex \ 26 | fluidsynth \ 27 | fonts-liberation \ 28 | gifsicle \ 29 | git \ 30 | imagemagick \ 31 | inkscape \ 32 | jed \ 33 | libboost-all-dev \ 34 | libdirectfb-dev \ 35 | libgl1-mesa-dev \ 36 | libjpeg-dev \ 37 | libpng-dev \ 38 | libpq-dev \ 39 | libsdl-sge-dev \ 40 | libsdl2-dev \ 41 | libsdl2-gfx-dev \ 42 | libsdl2-image-dev \ 43 | libsdl2-ttf-dev \ 44 | libsm6 \ 45 | libst-dev \ 46 | libxext-dev \ 47 | libxrender1 \ 48 | lmodern \ 49 | locales \ 50 | mesa-utils \ 51 | nano \ 52 | netcat \ 53 | nodejs \ 54 | npm \ 55 | pandoc \ 56 | python-dev \ 57 | python3-dev \ 58 | python3-lxml \ 59 | python3-pip \ 60 | python3-six \ 61 | python3-tk \ 62 | run-one \ 63 | sudo \ 64 | swig \ 65 | texlive-fonts-extra \ 66 | texlive-fonts-recommended \ 67 | texlive-generic-recommended \ 68 | texlive-latex-base \ 69 | texlive-latex-extra \ 70 | texlive-plain-generic \ 71 | texlive-xetex \ 72 | tzdata \ 73 | unzip \ 74 | vim \ 75 | vim-tiny \ 76 | wget \ 77 | x11vnc \ 78 | xpra \ 79 | xvfb \ 80 | && apt-get clean \ 81 | && rm -rf /var/lib/apt/lists/* 82 | 83 | RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \ 84 | locale-gen 85 | 86 | # Configure environment 87 | ENV CONDA_DIR=/opt/conda \ 88 | SHELL=/bin/bash \ 89 | NB_USER=$NB_USER \ 90 | NB_UID=$NB_UID \ 91 | NB_GID=$NB_GID \ 92 | LC_ALL=en_US.UTF-8 \ 93 | LANG=en_US.UTF-8 \ 94 | LANGUAGE=en_US.UTF-8 95 | ENV PATH=$CONDA_DIR/bin:$PATH \ 96 | HOME=/home/$NB_USER 97 | 98 | # Copy a script that we will use to correct permissions after running certain commands 99 | COPY fix-permissions /usr/local/bin/fix-permissions 100 | RUN chmod a+rx /usr/local/bin/fix-permissions 101 | 102 | # Enable prompt color in the skeleton .bashrc before creating the default NB_USER 103 | RUN sed -i 's/^#force_color_prompt=yes/force_color_prompt=yes/' /etc/skel/.bashrc 104 | 105 | # Create NB_USER with name jovyan user with UID=1000 and in the 'users' group 106 | # and make sure these dirs are writable by the `users` group. 107 | RUN echo "auth requisite pam_deny.so" >> /etc/pam.d/su && \ 108 | sed -i.bak -e 's/^%admin/#%admin/' /etc/sudoers && \ 109 | sed -i.bak -e 's/^%sudo/#%sudo/' /etc/sudoers && \ 110 | useradd -m -s /bin/bash -N -u $NB_UID $NB_USER && \ 111 | mkdir -p $CONDA_DIR && \ 112 | chown $NB_USER:$NB_GID $CONDA_DIR && \ 113 | chmod g+w /etc/passwd && \ 114 | fix-permissions $HOME && \ 115 | fix-permissions $CONDA_DIR 116 | 117 | USER $NB_UID 118 | WORKDIR $HOME 119 | ARG PYTHON_VERSION=3.7 120 | ARG WITH_TORCHVISION=1 121 | 122 | # Setup work directory for backward-compatibility 123 | RUN mkdir /home/$NB_USER/work && \ 124 | fix-permissions /home/$NB_USER 125 | 126 | # Install conda as jovyan and check the md5 sum provided on the download site 127 | ENV MINICONDA_VERSION=4.6.14 \ 128 | MINICONDA_MD5=718259965f234088d785cad1fbd7de03 \ 129 | CONDA_VERSION=4.6.14 130 | 131 | WORKDIR /tmp 132 | # https://repo.anaconda.com/miniconda/Miniconda3-4.6.14-Linux-x86_64.sh 133 | RUN wget --quiet https://repo.continuum.io/miniconda/Miniconda3-${MINICONDA_VERSION}-Linux-x86_64.sh && \ 134 | echo "${MINICONDA_MD5} *Miniconda3-${MINICONDA_VERSION}-Linux-x86_64.sh" | md5sum -c - && \ 135 | /bin/bash Miniconda3-${MINICONDA_VERSION}-Linux-x86_64.sh -f -b -p $CONDA_DIR && \ 136 | rm Miniconda3-${MINICONDA_VERSION}-Linux-x86_64.sh && \ 137 | echo "conda ${CONDA_VERSION}" >> $CONDA_DIR/conda-meta/pinned && \ 138 | conda config --system --prepend channels conda-forge && \ 139 | conda config --system --set auto_update_conda false && \ 140 | conda config --system --set show_channel_urls true && \ 141 | conda config --system --set channel_priority strict && \ 142 | if [ ! $PYTHON_VERSION = 'default' ]; then conda install --yes python=$PYTHON_VERSION; fi && \ 143 | conda list python | grep '^python ' | tr -s ' ' | cut -d '.' -f 1,2 | sed 's/$/.*/' >> $CONDA_DIR/conda-meta/pinned && \ 144 | conda install --quiet --yes conda && \ 145 | conda install --quiet --yes pip && \ 146 | conda update --all --quiet --yes && \ 147 | conda clean --all -f -y && \ 148 | rm -rf /home/$NB_USER/.cache/yarn && \ 149 | fix-permissions $CONDA_DIR && \ 150 | fix-permissions /home/$NB_USER 151 | 152 | # Install Tini 153 | RUN conda install --quiet --yes 'tini=0.18.0' && \ 154 | conda list tini | grep tini | tr -s ' ' | cut -d ' ' -f 1,2 >> $CONDA_DIR/conda-meta/pinned && \ 155 | conda clean --all -f -y && \ 156 | fix-permissions $CONDA_DIR && \ 157 | fix-permissions /home/$NB_USER 158 | 159 | # Install Jupyter Notebook, Lab, and Hub 160 | # Generate a notebook server config 161 | # Cleanup temporary files 162 | # Correct permissions 163 | # Do all this in a single RUN command to avoid duplicating all of the 164 | # files across image layers when the permissions change 165 | RUN conda install --quiet --yes \ 166 | 'notebook=6.0.3' \ 167 | 'jupyterhub=1.1.0' \ 168 | 'jupyterlab=2.1.3' && \ 169 | conda clean --all -f -y && \ 170 | npm cache clean --force && \ 171 | jupyter notebook --generate-config && \ 172 | rm -rf $CONDA_DIR/share/jupyter/lab/staging && \ 173 | rm -rf /home/$NB_USER/.cache/yarn && \ 174 | fix-permissions $CONDA_DIR && \ 175 | fix-permissions /home/$NB_USER 176 | 177 | # Configure container startup 178 | # ENTRYPOINT ["tini", "-g", "--"] 179 | # CMD ["start-notebook.sh"] 180 | 181 | # Copy local files as late as possible to avoid cache busting 182 | COPY start.sh start-notebook.sh start-singleuser.sh /usr/local/bin/ 183 | COPY jupyter_notebook_config.py /etc/jupyter/ 184 | 185 | # Fix permissions on /etc/jupyter as root 186 | USER root 187 | RUN fix-permissions /etc/jupyter/ 188 | 189 | # Switch back to jovyan to avoid accidental container runs as root 190 | USER $NB_UID 191 | 192 | # Install Python 3 packages 193 | # Remove pyqt and qt pulled in for matplotlib since we're only ever going to 194 | # use notebook-friendly backends in these images 195 | # Install Python 3 packages 196 | RUN conda install --quiet --yes \ 197 | 'beautifulsoup4=4.9.*' \ 198 | 'bokeh=2.0.*' \ 199 | 'bottleneck=1.3.*' \ 200 | 'cloudpickle=1.4.*' \ 201 | 'conda-forge::blas=*=openblas' \ 202 | 'cython=0.29.*' \ 203 | 'dask=2.15.*' \ 204 | 'dill=0.3.*' \ 205 | 'h5py=2.10.*' \ 206 | 'hdf5=1.10.*' \ 207 | 'ipympl=0.5.*'\ 208 | 'ipywidgets=7.5.*' \ 209 | 'matplotlib-base=3.2.*' \ 210 | 'numba=0.48.*' \ 211 | 'numexpr=2.7.*' \ 212 | 'pandas=1.0.*' \ 213 | 'patsy=0.5.*' \ 214 | 'protobuf=3.11.*' \ 215 | 'pytables=3.6.*' \ 216 | 'scikit-image=0.16.*' \ 217 | 'scikit-learn=0.22.*' \ 218 | 'scipy=1.4.*' \ 219 | 'seaborn=0.10.*' \ 220 | 'sqlalchemy=1.3.*' \ 221 | 'statsmodels=0.11.*' \ 222 | 'sympy=1.5.*' \ 223 | 'vincent=0.4.*' \ 224 | 'widgetsnbextension=3.5.*'\ 225 | 'xlrd=1.2.*' 226 | RUN conda clean --all -f -y 227 | # Activate ipywidgets extension in the environment that runs the notebook server 228 | RUN jupyter nbextension enable --py widgetsnbextension --sys-prefix 229 | # Also activate ipywidgets extension for JupyterLab 230 | # Check this URL for most recent compatibilities 231 | # https://github.com/jupyter-widgets/ipywidgets/tree/master/packages/jupyterlab-manager 232 | RUN jupyter labextension install @jupyter-widgets/jupyterlab-manager@^2.0.0 --no-build 233 | RUN jupyter labextension install @bokeh/jupyter_bokeh@^2.0.0 --no-build 234 | RUN jupyter labextension install jupyter-matplotlib@^0.7.2 --no-build 235 | RUN jupyter lab build -y --dev-build=False --minimize=False 236 | RUN jupyter lab clean -y && \ 237 | npm cache clean --force && \ 238 | rm -rf "/home/${NB_USER}/.cache/yarn" && \ 239 | rm -rf "/home/${NB_USER}/.node-gyp" && \ 240 | fix-permissions "${CONDA_DIR}" && \ 241 | fix-permissions "/home/${NB_USER}" 242 | 243 | RUN npm i -g npm && \ 244 | npm i -g asciicast2gif 245 | 246 | # Install facets which does not have a pip or conda package at the moment 247 | WORKDIR /tmp 248 | RUN git clone https://github.com/PAIR-code/facets.git && \ 249 | jupyter nbextension install facets/facets-dist/ --sys-prefix && \ 250 | rm -rf /tmp/facets && \ 251 | fix-permissions "${CONDA_DIR}" && \ 252 | fix-permissions "/home/${NB_USER}" 253 | 254 | # Import matplotlib the first time to build the font cache. 255 | ENV XDG_CACHE_HOME="/home/${NB_USER}/.cache/" 256 | 257 | RUN MPLBACKEND=Agg python -c "import matplotlib.pyplot" && \ 258 | fix-permissions "/home/${NB_USER}" 259 | 260 | RUN /opt/conda/bin/conda install -y python=$PYTHON_VERSION numpy pyyaml scipy ipython mkl mkl-include ninja cython typing && \ 261 | /opt/conda/bin/conda install -y -c pytorch magma-cuda100 && \ 262 | /opt/conda/bin/conda install -y -q -c pytorch pytorch torchvision && \ 263 | /opt/conda/bin/conda clean -ya 264 | ENV PATH /opt/conda/bin:$PATH 265 | 266 | # jupyter notebook 267 | EXPOSE 8888 268 | # tensorboard 269 | EXPOSE 6006 270 | 271 | # install necessary packages 272 | RUN pip install --upgrade pip && \ 273 | pip install --quiet --no-cache-dir tqdm cvxpy numpy scikit-learn pyglet setuptools && \ 274 | pip install --quiet --no-cache-dir gym asciinema pandas tabulate tornado==5.* && \ 275 | pip install --quiet --no-cache-dir PyBullet tensorflow==2.2.0 && \ 276 | pip install --quiet --no-cache-dir psutil && \ 277 | pip install --quiet --no-cache-dir git+https://github.com/pybox2d/pybox2d#egg=Box2D && \ 278 | pip install --quiet --no-cache-dir git+https://github.com/mimoralea/gym-bandits#egg=gym-bandits && \ 279 | pip install --quiet --no-cache-dir git+https://github.com/mimoralea/gym-walk#egg=gym-walk && \ 280 | pip install --quiet --no-cache-dir git+https://github.com/mimoralea/gym-aima#egg=gym-aima && \ 281 | pip install --quiet --no-cache-dir gym[atari] 282 | 283 | WORKDIR /tmp 284 | RUN cd /tmp && \ 285 | git clone https://github.com/google-research/football.git && \ 286 | cd football && \ 287 | pip install . && \ 288 | cd && \ 289 | rm -rf /tmp/football && \ 290 | fix-permissions $CONDA_DIR && \ 291 | fix-permissions /home/$NB_USER 292 | 293 | COPY policy.xml /etc/ImageMagick-6/ 294 | COPY 1_vs_1.py /opt/conda/lib/python3.7/site-packages/gfootball/scenarios/ 295 | COPY 2_vs_2.py /opt/conda/lib/python3.7/site-packages/gfootball/scenarios/ 296 | COPY 2_vs_2_auto_GK.py /opt/conda/lib/python3.7/site-packages/gfootball/scenarios/ 297 | COPY 3_vs_3.py /opt/conda/lib/python3.7/site-packages/gfootball/scenarios/ 298 | COPY 3_vs_3_auto_GK.py /opt/conda/lib/python3.7/site-packages/gfootball/scenarios/ 299 | COPY 4_vs_4.py /opt/conda/lib/python3.7/site-packages/gfootball/scenarios/ 300 | COPY 4_vs_4_auto_GK.py /opt/conda/lib/python3.7/site-packages/gfootball/scenarios/ 301 | 302 | COPY 5_vs_5.py /opt/conda/lib/python3.7/site-packages/gfootball/scenarios/ 303 | COPY 5_vs_5_auto_GK.py /opt/conda/lib/python3.7/site-packages/gfootball/scenarios/ 304 | 305 | RUN pip install --upgrade pip && \ 306 | pip uninstall -y tensorflow && \ 307 | pip uninstall -y tensorflow-tensorboard && \ 308 | pip uninstall -y tensorboard && \ 309 | pip install -U tensorboardX && \ 310 | pip install dm-tree lz4 gputil psutil && \ 311 | pip install -U ray ray[rllib] ray[tune] ray[debug] && \ 312 | pip install -U tensorboard && \ 313 | pip install -U tensorflow && \ 314 | pip install -U protobuf 315 | 316 | # create a script to start the notebook with xvfb on the back 317 | # this allows screen display to work well 318 | RUN echo '#!/bin/bash' > /tmp/run.sh && \ 319 | # echo "tensorboard --logdir /tmp/logs/ --bind_all &" >> /tmp/run.sh && \ 320 | # echo "nohup sh -c 'tensorboard --logdir=/tmp/logs/ --bind_all' &" >> /tmp/run.sh && \ 321 | echo "nohup sh -c 'ray start --head --include-dashboard true --dashboard-host 0.0.0.0' > /dev/null 2>&1 &" >> /tmp/run.sh && \ 322 | echo "nohup sh -c 'tensorboard --logdir=/tmp/logs/ --bind_all' > /dev/null 2>&1 &" >> /tmp/run.sh && \ 323 | echo 'find /mnt/notebooks/ -name "*.ipynb" -exec jupyter trust {} \;' >> /tmp/run.sh && \ 324 | echo 'xvfb-run -s "-screen 0 1280x720x24" /usr/local/bin/start-notebook.sh' >> /tmp/run.sh && \ 325 | chmod +x /tmp/run.sh 326 | 327 | # make the dir with notebooks the working dir 328 | WORKDIR /mnt/notebooks 329 | 330 | # run the script to start the notebook 331 | ENTRYPOINT ["/tmp/run.sh"] 332 | --------------------------------------------------------------------------------