├── .gcloudignore ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── docker ├── README.md ├── bash.sh ├── bashsc.sh ├── build.sh ├── build_cpu.sh ├── build_gpu.sh ├── cloudbuild.yaml ├── cpu.Dockerfile ├── gpu.Dockerfile ├── nodesource_setup_14.x.sh ├── open_spiel.Dockerfile ├── run.sh ├── runsc.sh └── test.sh ├── kaggle_environments ├── __init__.py ├── agent.py ├── api.py ├── core.py ├── envs │ ├── chess │ │ ├── chess.js │ │ ├── chess.json │ │ ├── chess.py │ │ └── test_chess.py │ ├── connectx │ │ ├── connectx.ipynb │ │ ├── connectx.js │ │ ├── connectx.json │ │ ├── connectx.py │ │ └── test_connectx.py │ ├── football │ │ ├── football.ipynb │ │ ├── football.json │ │ ├── football.py │ │ ├── helpers.py │ │ └── test_football.py │ ├── halite │ │ ├── __init__.py │ │ ├── halite.ipynb │ │ ├── halite.js │ │ ├── halite.json │ │ ├── halite.py │ │ ├── helpers.py │ │ └── test_halite.py │ ├── hungry_geese │ │ ├── __init__.py │ │ ├── hungry_geese.js │ │ ├── hungry_geese.json │ │ ├── hungry_geese.py │ │ └── test_hungry_geese.py │ ├── identity │ │ ├── identity.json │ │ └── identity.py │ ├── kore_fleets │ │ ├── __init__.py │ │ ├── helpers.py │ │ ├── kore_fleets.ipynb │ │ ├── kore_fleets.js │ │ ├── kore_fleets.json │ │ ├── kore_fleets.py │ │ ├── starter_bots │ │ │ ├── java │ │ │ │ ├── Bot.java │ │ │ │ ├── README.md │ │ │ │ ├── jars │ │ │ │ │ ├── hamcrest-core-1.3.jar │ │ │ │ │ └── junit-4.13.2.jar │ │ │ │ ├── kore │ │ │ │ │ ├── Board.java │ │ │ │ │ ├── Cell.java │ │ │ │ │ ├── Configuration.java │ │ │ │ │ ├── Direction.java │ │ │ │ │ ├── Fleet.java │ │ │ │ │ ├── KoreJson.java │ │ │ │ │ ├── Observation.java │ │ │ │ │ ├── Pair.java │ │ │ │ │ ├── Player.java │ │ │ │ │ ├── Point.java │ │ │ │ │ ├── Shipyard.java │ │ │ │ │ └── ShipyardAction.java │ │ │ │ ├── main.py │ │ │ │ └── test │ │ │ │ │ ├── BoardTest.java │ │ │ │ │ ├── ConfigurationTest.java │ │ │ │ │ ├── KoreJsonTest.java │ │ │ │ │ ├── ObservationTest.java │ │ │ │ │ ├── PointTest.java │ │ │ │ │ ├── ShipyardTest.java │ │ │ │ │ ├── configuration.json │ │ │ │ │ ├── fullob.json │ │ │ │ │ └── observation.json │ │ │ ├── python │ │ │ │ ├── __init__.py │ │ │ │ └── main.py │ │ │ └── ts │ │ │ │ ├── Bot.ts │ │ │ │ ├── DoNothingBot.ts │ │ │ │ ├── MinerBot.ts │ │ │ │ ├── README.md │ │ │ │ ├── interpreter.ts │ │ │ │ ├── kore │ │ │ │ ├── Board.ts │ │ │ │ ├── Cell.ts │ │ │ │ ├── Configuration.ts │ │ │ │ ├── Direction.ts │ │ │ │ ├── Fleet.ts │ │ │ │ ├── KoreIO.ts │ │ │ │ ├── Observation.ts │ │ │ │ ├── Pair.ts │ │ │ │ ├── Player.ts │ │ │ │ ├── Point.ts │ │ │ │ ├── Shipyard.ts │ │ │ │ └── ShipyardAction.ts │ │ │ │ ├── main.py │ │ │ │ ├── miner.py │ │ │ │ ├── package.json │ │ │ │ ├── test │ │ │ │ ├── BoardTest.ts │ │ │ │ ├── ConfigurationTest.ts │ │ │ │ ├── ObservationTest.ts │ │ │ │ ├── PointTest.ts │ │ │ │ ├── ShipyardTest.ts │ │ │ │ ├── configuration.json │ │ │ │ ├── fullob.json │ │ │ │ └── observation.json │ │ │ │ └── tsconfig.json │ │ └── test_kore_fleets.py │ ├── llm_20_questions │ │ ├── keywords.py │ │ ├── llm_20_questions.js │ │ ├── llm_20_questions.json │ │ ├── llm_20_questions.py │ │ └── test_llm_20_questions.py │ ├── lux_ai_2021 │ │ ├── README.md │ │ ├── __init__.py │ │ ├── agents.py │ │ ├── dimensions │ │ │ ├── 754.js │ │ │ ├── 754.js.LICENSE.txt │ │ │ └── main.js │ │ ├── index.html │ │ ├── lux_ai_2021.json │ │ ├── lux_ai_2021.py │ │ ├── test_agents │ │ │ ├── __init__.py │ │ │ ├── js_simple │ │ │ │ ├── lux │ │ │ │ │ ├── game_constants.js │ │ │ │ │ ├── game_constants.json │ │ │ │ │ ├── game_objects.js │ │ │ │ │ ├── io.js │ │ │ │ │ ├── kit.js │ │ │ │ │ ├── map.js │ │ │ │ │ └── parser.js │ │ │ │ ├── main.js │ │ │ │ ├── main.py │ │ │ │ └── simple.tar.gz │ │ │ └── python │ │ │ │ ├── lux │ │ │ │ ├── __init__.py │ │ │ │ ├── annotate.py │ │ │ │ ├── constants.py │ │ │ │ ├── game.py │ │ │ │ ├── game_constants.json │ │ │ │ ├── game_constants.py │ │ │ │ ├── game_map.py │ │ │ │ └── game_objects.py │ │ │ │ ├── random_agent.py │ │ │ │ └── simple_agent.py │ │ ├── test_lux.py │ │ ├── testing.md │ │ └── todo.md.og │ ├── lux_ai_s2 │ │ ├── .gitignore │ │ ├── README.md │ │ ├── __init__.py │ │ ├── agents.py │ │ ├── index.html │ │ ├── lux_ai_s2.json │ │ ├── lux_ai_s2.py │ │ ├── luxai_s2 │ │ │ ├── .DS_Store │ │ │ ├── __init__.py │ │ │ ├── actions.py │ │ │ ├── config.py │ │ │ ├── env.py │ │ │ ├── factory.py │ │ │ ├── globals.py │ │ │ ├── map │ │ │ │ ├── __init__.py │ │ │ │ ├── bfs_deltas_gen.py │ │ │ │ ├── board.py │ │ │ │ └── position.py │ │ │ ├── map_generator │ │ │ │ ├── .DS_Store │ │ │ │ ├── __init__.py │ │ │ │ ├── generator.py │ │ │ │ ├── symnoise.py │ │ │ │ ├── timing.py │ │ │ │ ├── visualize.py │ │ │ │ └── visualize_samples.py │ │ │ ├── pyvisual │ │ │ │ ├── __init__.py │ │ │ │ └── visualizer.py │ │ │ ├── replay │ │ │ │ ├── __init__.py │ │ │ │ └── replay.py │ │ │ ├── spaces │ │ │ │ ├── __init__.py │ │ │ │ ├── act_space.py │ │ │ │ └── obs_space.py │ │ │ ├── state │ │ │ │ ├── __init__.py │ │ │ │ ├── state.py │ │ │ │ ├── stats.py │ │ │ │ └── typing.py │ │ │ ├── team.py │ │ │ ├── tools │ │ │ │ ├── __init__.py │ │ │ │ └── replay.py │ │ │ ├── unit.py │ │ │ ├── utils │ │ │ │ ├── __init__.py │ │ │ │ ├── animate.py │ │ │ │ ├── heuristics │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── bidding.py │ │ │ │ │ ├── factory.py │ │ │ │ │ └── factory_placement.py │ │ │ │ └── utils.py │ │ │ ├── version.py │ │ │ └── wrappers │ │ │ │ ├── __init__.py │ │ │ │ ├── controllers.py │ │ │ │ └── sb3.py │ │ ├── test_agents │ │ │ └── python │ │ │ │ ├── agent.py │ │ │ │ ├── lux │ │ │ │ ├── cargo.py │ │ │ │ ├── config.py │ │ │ │ ├── factory.py │ │ │ │ ├── kit.py │ │ │ │ ├── team.py │ │ │ │ ├── unit.py │ │ │ │ └── utils.py │ │ │ │ └── main.py │ │ └── test_lux.py │ ├── lux_ai_s3 │ │ ├── README.md │ │ ├── agents.py │ │ ├── index.html │ │ ├── lux_ai_s3.json │ │ ├── lux_ai_s3.py │ │ ├── luxai_s3 │ │ │ ├── __init__.py │ │ │ ├── env.py │ │ │ ├── globals.py │ │ │ ├── params.py │ │ │ ├── profiler.py │ │ │ ├── pygame_render.py │ │ │ ├── spaces.py │ │ │ ├── state.py │ │ │ ├── utils.py │ │ │ └── wrappers.py │ │ ├── test_agents │ │ │ └── python │ │ │ │ ├── agent.py │ │ │ │ ├── lux │ │ │ │ ├── __init__.py │ │ │ │ ├── kit.py │ │ │ │ └── utils.py │ │ │ │ └── main.py │ │ └── test_lux.py │ ├── mab │ │ ├── __init__.py │ │ ├── agents.py │ │ ├── mab.js │ │ ├── mab.json │ │ └── mab.py │ ├── open_spiel │ │ ├── open_spiel.py │ │ └── test_open_spiel.py │ ├── rps │ │ ├── __init__.py │ │ ├── agents.py │ │ ├── helpers.py │ │ ├── rps.js │ │ ├── rps.json │ │ ├── rps.py │ │ ├── test_rps.py │ │ └── utils.py │ └── tictactoe │ │ ├── test_tictactoe.py │ │ ├── tictactoe.ipynb │ │ ├── tictactoe.js │ │ ├── tictactoe.json │ │ └── tictactoe.py ├── errors.py ├── helpers.py ├── main.py ├── schemas.json ├── static │ └── player.html ├── status_codes.json └── utils.py ├── package.sh ├── release.sh ├── requirements.txt └── setup.py /.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | # If you would like to upload your .git directory, .gitignore file or files 11 | # from your .gitignore file, remove the corresponding line 12 | # below: 13 | .git 14 | .gitignore 15 | 16 | node_modules 17 | #!include:.gitignore 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # pyenv 10 | bin/ 11 | lib64 12 | pyvenv.cfg 13 | share/ 14 | 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | pip-wheel-metadata/ 31 | share/python-wheels/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | MANIFEST 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .nox/ 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | *.py,cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 102 | __pypackages__/ 103 | 104 | # Celery stuff 105 | celerybeat-schedule 106 | celerybeat.pid 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | !docker/envs/football/egl/gfootball/env/ 120 | 121 | # Spyder project settings 122 | .spyderproject 123 | .spyproject 124 | 125 | # Rope project settings 126 | .ropeproject 127 | 128 | # mkdocs documentation 129 | /site 130 | 131 | # mypy 132 | .mypy_cache/ 133 | .dmypy.json 134 | dmypy.json 135 | 136 | # Pyre type checker 137 | .pyre/ 138 | 139 | # Temp Files 140 | temp.* 141 | 142 | .idea/ 143 | 144 | # Git merge artifacts 145 | *.orig 146 | 147 | # node.js files 148 | node_modules 149 | 150 | # Java class files 151 | *.class 152 | 153 | .vscode 154 | 155 | submission.tar.gz 156 | 157 | replay.html 158 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows 28 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | recursive-include kaggle_environments *.py 3 | recursive-include kaggle_environments *.html 4 | recursive-include kaggle_environments *.js 5 | recursive-include kaggle_environments *.ts 6 | global-exclude */node_modules 7 | recursive-include kaggle_environments *.json -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Docker Image 2 | This image encapsulates the kaggle-environments library, its dependencies, and agent execution environment. 3 | This image is hosted at `gcr.io/kaggle-images/python-simulations` 4 | 5 | ## Usage 6 | * `./build.sh` will build the image including any local changes to kaggle_environments. 7 | * `./run.sh` will pass any arguments to the kaggle-environments command line tool running in docker. Note that this also binds port 8080 to run `kaggle-environments http-server` commands. 8 | * `./run.sh list` 9 | * `./run.sh run --environment connectx --agent random random` 10 | * `./run.sh http-server` 11 | * `./test.sh` will run the unit test suite in docker with pytest. 12 | * `./test.sh -k "halite"` -------------------------------------------------------------------------------- /docker/bash.sh: -------------------------------------------------------------------------------- 1 | # Start the orchestrator container in Docker 2 | docker run -it --entrypoint //bin/bash --rm --name python-simulations python-simulations-cpu -------------------------------------------------------------------------------- /docker/bashsc.sh: -------------------------------------------------------------------------------- 1 | # Start the orchestrator container in gVisor 2 | docker run --runtime=runsc -it --cpus="0.8" --memory="4g" --entrypoint //bin/bash --rm --name python-simulations python-simulations-cpu -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | # Just build the orchestrator container 2 | path=$(dirname $0) 3 | ./$path/build_cpu.sh 4 | # We don't use the GPU image yet, uncomment when used. 5 | # ./$path/build_gpu.sh -------------------------------------------------------------------------------- /docker/build_cpu.sh: -------------------------------------------------------------------------------- 1 | # Just build the orchestrator container 2 | path=$(dirname $0) 3 | # cd to the parent directory to include kaggle_environments folder in Docker build context 4 | cd $path/.. 5 | docker build --pull -f ./docker/cpu.Dockerfile -t python-simulations-cpu . 6 | cd - 7 | -------------------------------------------------------------------------------- /docker/build_gpu.sh: -------------------------------------------------------------------------------- 1 | # Just build the orchestrator container 2 | path=$(dirname $0) 3 | # cd to the parent directory to include kaggle_environments folder in Docker build context 4 | cd $path/.. 5 | docker build -f ./docker/gpu.Dockerfile -t python-simulations-gpu . 6 | cd - -------------------------------------------------------------------------------- /docker/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - id: 'build-cpu-image' 3 | name: gcr.io/cloud-builders/docker 4 | args: ['build', '-f', './docker/cpu.Dockerfile', '-t', 'gcr.io/kaggle-images/python-simulations', '.'] 5 | - id: 'build-gpu-image' 6 | name: gcr.io/cloud-builders/docker 7 | args: ['build', '-f', './docker/gpu.Dockerfile', '-t', 'gcr.io/kaggle-gpu-images/python-simulations', '.'] 8 | images: ['gcr.io/kaggle-images/python-simulations', 'gcr.io/kaggle-gpu-images/python-simulations'] 9 | timeout: 24h -------------------------------------------------------------------------------- /docker/cpu.Dockerfile: -------------------------------------------------------------------------------- 1 | # DANGER -- If you update this file, make sure to also update gpu.Dockerfile! 2 | 3 | FROM gcr.io/kaggle-images/python:latest 4 | 5 | # NODE 6 | 7 | # install node and npm from nodesource https://github.com/nodesource/distributions 8 | # use a local mirror of the setup script to avoid `curl | bash` 9 | ADD docker/nodesource_setup_14.x.sh node_setup.sh 10 | RUN sh node_setup.sh && apt-get install -y nodejs 11 | 12 | # link the newly installed versions to /opt/node so we can prioritize these versions over the versions /opt/conda has. 13 | RUN mkdir /opt/node && \ 14 | ln -s /usr/bin/node /opt/node/ && \ 15 | ln -s /usr/bin/npm /opt/node/ 16 | 17 | # add node and npm to path so the commands are available 18 | ENV PATH /opt/node:$PATH 19 | ENV NODE_PATH /usr/lib/node_modules 20 | 21 | # confirm installation 22 | RUN node -v && npm -v 23 | 24 | # END NODE 25 | 26 | WORKDIR /usr/src/app/kaggle_environments 27 | 28 | ADD ./setup.py ./setup.py 29 | ADD ./README.md ./README.md 30 | ADD ./MANIFEST.in ./MANIFEST.in 31 | ADD ./kaggle_environments ./kaggle_environments 32 | 33 | 34 | # install kaggle-environments 35 | RUN pip install Flask bitsandbytes accelerate vec-noise jax gymnax==0.0.8 && pip install . && pytest 36 | 37 | # SET UP KAGGLE-ENVIRONMENTS CHESS 38 | # minimal package to reduce memory footprint 39 | RUN mkdir ./kaggle_environments_chess 40 | RUN cp -r ./kaggle_environments/* ./kaggle_environments_chess/ 41 | RUN rm -rf ./kaggle_environments 42 | # remove other runtimes 43 | RUN find ./kaggle_environments_chess/envs -mindepth 1 -maxdepth 1 ! -name "chess" -type d -exec rm -rf {} + 44 | # pyclean 45 | RUN rm -rf ./kaggle_environments_chess/__pycache__; rm -rf ./kaggle_environments_chess/envs/__pycache__; rm -rf ./kaggle_environments_chess/envs/chess/__pycache__; true 46 | RUN find ./kaggle_environments_chess/ -name "*.pyc" -exec rm -f {} \; 47 | 48 | # rename pip package 49 | RUN sed -i 's/kaggle-environments/kaggle-environments-chess/g' ./setup.py 50 | RUN sed -i 's/kaggle_environments/kaggle_environments_chess/g' ./setup.py 51 | RUN sed -i 's/kaggle_environments/kaggle_environments_chess/g' ./MANIFEST.in 52 | 53 | # install kaggle-environments-chess 54 | RUN pip install . && pytest 55 | 56 | CMD kaggle-environments 57 | -------------------------------------------------------------------------------- /docker/gpu.Dockerfile: -------------------------------------------------------------------------------- 1 | # DANGER -- If you update this file, make sure to also update cpu.Dockerfile! 2 | 3 | FROM gcr.io/kaggle-gpu-images/python:latest 4 | 5 | # NODE 6 | 7 | # install node and npm from nodesource https://github.com/nodesource/distributions 8 | # use a local mirror of the setup script to avoid `curl | bash` 9 | ADD docker/nodesource_setup_14.x.sh node_setup.sh 10 | RUN sh node_setup.sh && apt-get install -y nodejs 11 | 12 | # link the newly installed versions to /opt/node so we can prioritize these versions over the versions /opt/conda has. 13 | RUN mkdir /opt/node && \ 14 | ln -s /usr/bin/node /opt/node/ && \ 15 | ln -s /usr/bin/npm /opt/node/ 16 | 17 | # add node and npm to path so the commands are available 18 | ENV PATH /opt/node:$PATH 19 | ENV NODE_PATH /usr/lib/node_modules 20 | 21 | # confirm installation 22 | RUN node -v && npm -v 23 | 24 | # END NODE 25 | 26 | WORKDIR /usr/src/app/kaggle_environments 27 | 28 | ADD ./setup.py ./setup.py 29 | ADD ./README.md ./README.md 30 | ADD ./MANIFEST.in ./MANIFEST.in 31 | ADD ./kaggle_environments ./kaggle_environments 32 | RUN pip install Flask bitsandbytes accelerate vec-noise jax gymnax==0.0.8 && pip install . && pytest 33 | 34 | CMD kaggle-environments 35 | -------------------------------------------------------------------------------- /docker/run.sh: -------------------------------------------------------------------------------- 1 | # Start the orchestrator container in Docker 2 | docker run -it --entrypoint kaggle-environments --rm -p 127.0.0.1:8080:8080/tcp --name python-simulations python-simulations-cpu "$@" -------------------------------------------------------------------------------- /docker/runsc.sh: -------------------------------------------------------------------------------- 1 | # Start the orchestrator container in gVisor 2 | docker run --runtime=runsc -it --cpus="0.8" --memory="4g" --entrypoint kaggle-environments --rm -p 127.0.0.1:8080:8080/tcp --name python-simulations python-simulations-cpu "$@" -------------------------------------------------------------------------------- /docker/test.sh: -------------------------------------------------------------------------------- 1 | # Start the orchestrator container in Docker 2 | # The double slash in the test directory is to force it to translate to a Linux path rather than a Windows path 3 | docker run -it --entrypoint pytest --rm --name python-simulations python-simulations-cpu '/usr/src/app/kaggle_environments' "$@" 4 | -------------------------------------------------------------------------------- /kaggle_environments/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Kaggle Inc 2 | # 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 | from importlib import import_module 16 | from os import listdir 17 | from .agent import Agent 18 | from .api import get_episode_replay, list_episodes, list_episodes_for_team, list_episodes_for_submission 19 | from .core import * 20 | from .main import http_request 21 | from . import errors 22 | 23 | __version__ = "1.17.2" 24 | 25 | __all__ = ["Agent", "environments", "errors", "evaluate", "http_request", 26 | "make", "register", "utils", "__version__", 27 | "get_episode_replay", "list_episodes", "list_episodes_for_team", "list_episodes_for_submission"] 28 | 29 | # Register Environments. 30 | 31 | for name in listdir(utils.envs_path): 32 | try: 33 | env = import_module(f".envs.{name}.{name}", __name__) 34 | if name == "open_spiel": 35 | for env_name, env_dict in env.registered_open_spiel_envs.items(): 36 | register(env_name, { 37 | "agents": env_dict.get("agents"), 38 | "html_renderer": env_dict.get("html_renderer"), 39 | "interpreter": env_dict.get("interpreter"), 40 | "renderer": env_dict.get("renderer"), 41 | "specification": env_dict.get("specification"), 42 | }) 43 | else: 44 | register(name, { 45 | "agents": getattr(env, "agents", []), 46 | "html_renderer": getattr(env, "html_renderer", None), 47 | "interpreter": getattr(env, "interpreter"), 48 | "renderer": getattr(env, "renderer"), 49 | "specification": getattr(env, "specification"), 50 | }) 51 | except Exception as e: 52 | if "football" not in name: 53 | print("Loading environment %s failed: %s" % (name, e)) 54 | -------------------------------------------------------------------------------- /kaggle_environments/api.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from typing import * 4 | 5 | base_url = "https://www.kaggle.com/requests/EpisodeService/" 6 | get_url = base_url + "GetEpisodeReplay" 7 | list_url = base_url + "ListEpisodes" 8 | 9 | 10 | def get_episode_replay(episode_id: int): 11 | body = { 12 | "EpisodeId": episode_id 13 | } 14 | 15 | response = requests.post(get_url, json=body) 16 | return response.json() 17 | 18 | 19 | def list_episodes(episode_ids: List[int]): 20 | return __list_episodes({ 21 | "Ids": episode_ids 22 | }) 23 | 24 | 25 | def list_episodes_for_team(team_id: int): 26 | return __list_episodes({ 27 | "TeamId": team_id 28 | }) 29 | 30 | 31 | def list_episodes_for_submission(submission_id: int): 32 | return __list_episodes({ 33 | "SubmissionId": submission_id 34 | }) 35 | 36 | 37 | def __list_episodes(body): 38 | response = requests.post(list_url, json=body) 39 | return response.json() 40 | -------------------------------------------------------------------------------- /kaggle_environments/envs/chess/chess.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chess", 3 | "title": "Chess", 4 | "description": "Classic Chess with full rule set", 5 | "version": "1.0.0", 6 | "agents": [2], 7 | "configuration": { 8 | "episodeSteps": 1000, 9 | "actTimeout": 0.1, 10 | "runTimeout": 300, 11 | "seed": { 12 | "description": "Integer random value to use to seed the match", 13 | "type": "number", 14 | "default": 0 15 | }, 16 | "agentTimeout": { 17 | "description": "Obsolete field kept for backwards compatibility, please use observation.remainingOverageTime.", 18 | "type": "number", 19 | "minimum": 0, 20 | "default": 10 21 | } 22 | }, 23 | "reward": { 24 | "description": "0 = Lost/Ongoing, 0.5 = Draw, 1 = Won", 25 | "enum": [0, 0.5, 1], 26 | "default": 0 27 | }, 28 | "observation": { 29 | "board": { 30 | "description": "FEN string representation of the board", 31 | "type": "string", 32 | "shared": true, 33 | "default": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" 34 | }, 35 | "mark": { 36 | "description": "Player color, white or black", 37 | "defaults": ["white", "black"], 38 | "enum": ["white", "black"] 39 | }, 40 | "opponentRemainingOverageTime": { 41 | "description": "Amount of overage time remaining for the opponent.", 42 | "type": "number", 43 | "default": 10 44 | }, 45 | "lastMove": { 46 | "description": "Previous move to get to this position.", 47 | "type": "string", 48 | "default": "" 49 | }, 50 | "remainingOverageTime": 10 51 | }, 52 | "action": { 53 | "description": "Move in UCI notation (e.g., e2e4)", 54 | "type": "string", 55 | "default": "" 56 | }, 57 | "status": { 58 | "defaults": ["ACTIVE", "INACTIVE"] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /kaggle_environments/envs/chess/test_chess.py: -------------------------------------------------------------------------------- 1 | from kaggle_environments import make 2 | from Chessnut import Game 3 | from chess import is_insufficient_material 4 | 5 | def test_chess_inits(): 6 | env = make("chess", debug=True) 7 | env.run(["random", "random"]) 8 | json = env.toJSON() 9 | assert json["name"] == "chess" 10 | assert json["statuses"] == ["DONE", "DONE"] 11 | 12 | def test_chess_three_fold(): 13 | env = make("chess", debug=True) 14 | env.run(["king_shuffle", "king_shuffle"]) 15 | json = env.toJSON() 16 | assert json["name"] == "chess" 17 | assert json["statuses"] == ["DONE", "DONE"] 18 | assert json["rewards"] == [0.5, 0.5] 19 | 20 | def test_chess_100_move_rule(): 21 | env = make("chess", debug=True) 22 | env.run(["board_shuffle", "board_shuffle"]) 23 | json = env.toJSON() 24 | assert json["name"] == "chess" 25 | assert json["statuses"] == ["DONE", "DONE"] 26 | assert json["rewards"] == [0.5, 0.5] 27 | 28 | def test_sufficient_material(): 29 | game = Game() 30 | assert not is_insufficient_material(game.board) 31 | 32 | def test_insufficient_material_with_two_kings(): 33 | game = Game('8/8/K7/8/8/3k4/8/8 w - - 58 282') 34 | assert is_insufficient_material(game.board) 35 | 36 | def test_insufficient_material_with_two_kings_and_bishop(): 37 | game = Game('6k1/8/7B/8/8/8/8/2K5 b - - 90 250') 38 | assert is_insufficient_material(game.board) 39 | 40 | def test_insufficient_material_with_two_kings_and_two_knights(): 41 | game = Game('6k1/8/6NN/8/8/8/8/2K5 b - - 90 250') 42 | assert is_insufficient_material(game.board) 43 | 44 | def test_sufficient_material_with_king_knight_and_bishop(): 45 | game = Game('6k1/8/6NB/8/8/8/8/2K5 b - - 90 250') 46 | assert not is_insufficient_material(game.board) 47 | 48 | def test_sufficient_material_with_king_bishop_and_bishop(): 49 | game = Game('6k1/8/6BB/8/8/8/8/2K5 b - - 90 250') 50 | assert not is_insufficient_material(game.board) -------------------------------------------------------------------------------- /kaggle_environments/envs/connectx/connectx.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "connectx", 3 | "title": "ConnectX", 4 | "description": "Classic Connect in a row but configurable.", 5 | "version": "1.0.1", 6 | "agents": [2], 7 | "configuration": { 8 | "columns": { 9 | "description": "The number of columns on the board", 10 | "type": "integer", 11 | "default": 7, 12 | "minimum": 1 13 | }, 14 | "rows": { 15 | "description": "The number of rows on the board", 16 | "type": "integer", 17 | "default": 6, 18 | "minimum": 1 19 | }, 20 | "inarow": { 21 | "description": "The number of checkers in a row required to win.", 22 | "type": "integer", 23 | "default": 4, 24 | "minimum": 1 25 | }, 26 | "agentTimeout": { 27 | "description": "Obsolete field kept for backwards compatibility, please use observation.remainingOverageTime.", 28 | "type": "number", 29 | "minimum": 0, 30 | "default": 60 31 | }, 32 | "actTimeout": 2, 33 | "timeout": { 34 | "description": "Obsolete copy of actTimeout maintained for backwards compatibility. May be removed in the future.", 35 | "type": "integer", 36 | "default": 2, 37 | "minimum": 0 38 | } 39 | }, 40 | "reward": { 41 | "description": "-1 = Lost, 0 = Draw/Ongoing, 1 = Won", 42 | "enum": [-1, 0, 1], 43 | "default": 0 44 | }, 45 | "observation": { 46 | "board": { 47 | "description": "Serialized grid (rows x columns). 0 = Empty, 1 = P1, 2 = P2", 48 | "type": "array", 49 | "shared": true, 50 | "items": { 51 | "enum": [0, 1, 2] 52 | }, 53 | "default": [] 54 | }, 55 | "mark": { 56 | "defaults": [1, 2], 57 | "description": "Which checkers are the agents.", 58 | "enum": [1, 2] 59 | }, 60 | "remainingOverageTime": 60 61 | }, 62 | "action": { 63 | "description": "Column to drop a checker onto the board.", 64 | "type": "integer", 65 | "minimum": 0, 66 | "default": 0 67 | }, 68 | "status": { 69 | "defaults": ["ACTIVE", "INACTIVE"] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /kaggle_environments/envs/football/football.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "Kaggle football example", 7 | "provenance": [] 8 | }, 9 | "kernelspec": { 10 | "name": "python3", 11 | "display_name": "Python 3" 12 | } 13 | }, 14 | "cells": [ 15 | { 16 | "cell_type": "code", 17 | "metadata": { 18 | "id": "edvwsjsJsAQY", 19 | "colab_type": "code", 20 | "colab": {} 21 | }, 22 | "source": [ 23 | "## Installing kaggle-environments.\n", 24 | "!pip install kaggle-environments\n", 25 | "\n", 26 | "# 4. Import register to define the environment and make to create it.\n", 27 | "from kaggle_environments import make, register" 28 | ], 29 | "execution_count": 0, 30 | "outputs": [] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "metadata": { 35 | "id": "dWXjFxmDsiVb", 36 | "colab_type": "code", 37 | "colab": {} 38 | }, 39 | "source": [ 40 | "# Installing Google Football environment.\n", 41 | "\n", 42 | "!apt-get update\n", 43 | "!apt-get install -y libsdl2-gfx-dev libsdl2-ttf-dev\n", 44 | "\n", 45 | "# Make sure that the Branch in git clone and in wget call matches !!\n", 46 | "!git clone -b v2.3 https://github.com/google-research/football.git\n", 47 | "!mkdir -p football/third_party/gfootball_engine/lib\n", 48 | "\n", 49 | "!wget https://storage.googleapis.com/gfootball/prebuilt_gameplayfootball_v2.3.so -O football/third_party/gfootball_engine/lib/prebuilt_gameplayfootball.so\n", 50 | "!cd football && GFOOTBALL_USE_PREBUILT_SO=1 pip3 install ." 51 | ], 52 | "execution_count": 0, 53 | "outputs": [] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "metadata": { 58 | "id": "4URn2BRvs3qL", 59 | "colab_type": "code", 60 | "colab": {} 61 | }, 62 | "source": [ 63 | "env = make(\"football\", debug=True, configuration={\"scenario_name\": \"11_vs_11_kaggle\", \"team_1\": 1, \"team_2\": 1, \"running_in_notebook\": True, \"save_video\": True})\n", 64 | "print(env.name, env.version)\n", 65 | "print(\"Default Agents: \", *env.agents)\n", 66 | "\n", 67 | "env.run([\"run_right\", \"run_left\"])\n", 68 | " \n", 69 | "env.render(mode=\"html\", width=960, height=720)" 70 | ], 71 | "execution_count": 0, 72 | "outputs": [] 73 | } 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /kaggle_environments/envs/football/football.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "football", 3 | "agents": [2], 4 | "configuration": { 5 | "episodeSteps": 3002, 6 | "agentTimeout": { 7 | "description": "Obsolete field kept for backwards compatibility, please use observation.remainingOverageTime.", 8 | "type": "number", 9 | "minimum": 0, 10 | "default": 60 11 | }, 12 | "actTimeout": 0.5, 13 | "runTimeout": 12000, 14 | "scenario_name": { 15 | "description": "Name of the scenario: for example 11_vs_11_kaggle. Look inside https://github.com/google-research/football/tree/master/gfootball/scenarios.", 16 | "type": "string", 17 | "default": "11_vs_11_kaggle" 18 | }, 19 | "id": { 20 | "description": "Id of this environment run, a random uuid.", 21 | "type": "string", 22 | "default": null 23 | }, 24 | "team_1": { 25 | "description": "Number of players from the first team that agent controls.", 26 | "type": "integer", 27 | "minimum": 0, 28 | "maximum": 11, 29 | "default": 1 30 | }, 31 | "team_2": { 32 | "description": "Number of players that other agent controls. If set to 0 - the second agent will always have to return 0 as action.", 33 | "type": "integer", 34 | "minimum": 0, 35 | "maximum": 11, 36 | "default": 1 37 | }, 38 | "render": { 39 | "description": "If true - renders the game on the screen. This option will work on your local computer, but is not supported in colabs/notebooks.", 40 | "type": "boolean", 41 | "default": false 42 | }, 43 | "save_video": { 44 | "description": "If true, will record the video of the playthrough.", 45 | "type": "boolean", 46 | "default": false 47 | }, 48 | "logdir": { 49 | "description": "Directory to write the state dump and video information.", 50 | "type": "string", 51 | "default": "/tmp/football" 52 | }, 53 | "running_in_notebook": { 54 | "description": "Set to true, if you're creating this environment inside Kaggle/Colab notebook.", 55 | "type": "boolean", 56 | "default": false 57 | } 58 | }, 59 | "reward": { 60 | "description": "1.0 for scored goal, -1.0 for lost goal (it is given only when the goal is scored, and changes back to 0 afterwards)", 61 | "type": "number" 62 | }, 63 | "info": { 64 | "debug_info": { 65 | "description": "Human readable information passed from the system.", 66 | "type": "string" 67 | } 68 | }, 69 | "observation": { 70 | "players_raw": { 71 | "description": "Array of raw observations, one entry per each player that your agent controls. See https://github.com/google-research/football/blob/master/gfootball/doc/observation.md for detailed description. WARNING: your players will always look like they are 'playing from left to right' (to make training easier).", 72 | "type": "array" 73 | }, 74 | "controlled_players": { 75 | "description": "number of players that agent controls", 76 | "type": "number", 77 | "minimum": 0, 78 | "maximum": 11 79 | }, 80 | "remainingOverageTime": 60 81 | }, 82 | "action": { 83 | "description": "An action to execute for each player that agent controls: 0-idle, 1-left, 2-top_left, etc. See https://github.com/google-research/football/blob/master/gfootball/doc/observation.md", 84 | "type": "array", 85 | "items": { 86 | "type": "number", 87 | "minimum": 0, 88 | "maximum": 19 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /kaggle_environments/envs/football/helpers.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from enum import Enum 3 | from typing import * 4 | 5 | 6 | class Action(Enum): 7 | Idle = 0 8 | Left = 1 9 | TopLeft = 2 10 | Top = 3 11 | TopRight = 4 12 | Right = 5 13 | BottomRight = 6 14 | Bottom = 7 15 | BottomLeft = 8 16 | LongPass= 9 17 | HighPass = 10 18 | ShortPass = 11 19 | Shot = 12 20 | Sprint = 13 21 | ReleaseDirection = 14 22 | ReleaseSprint = 15 23 | Slide = 16 24 | Dribble = 17 25 | ReleaseDribble = 18 26 | 27 | 28 | sticky_index_to_action = [ 29 | Action.Left, 30 | Action.TopLeft, 31 | Action.Top, 32 | Action.TopRight, 33 | Action.Right, 34 | Action.BottomRight, 35 | Action.Bottom, 36 | Action.BottomLeft, 37 | Action.Sprint, 38 | Action.Dribble 39 | ] 40 | 41 | 42 | class PlayerRole(Enum): 43 | GoalKeeper = 0 44 | CenterBack = 1 45 | LeftBack = 2 46 | RightBack = 3 47 | DefenceMidfield = 4 48 | CentralMidfield = 5 49 | LeftMidfield = 6 50 | RIghtMidfield = 7 51 | AttackMidfield = 8 52 | CentralFront = 9 53 | 54 | 55 | class GameMode(Enum): 56 | Normal = 0 57 | KickOff = 1 58 | GoalKick = 2 59 | FreeKick = 3 60 | Corner = 4 61 | ThrowIn = 5 62 | Penalty = 6 63 | 64 | 65 | def human_readable_agent(agent: Callable[[Dict], Action]): 66 | """ 67 | Decorator allowing for more human-friendly implementation of the agent function. 68 | 69 | @human_readable_agent 70 | def my_agent(obs): 71 | ... 72 | return football_action_set.action_right 73 | """ 74 | @wraps(agent) 75 | def agent_wrapper(obs) -> List[int]: 76 | # Extract observations for the first (and only) player we control. 77 | obs = obs['players_raw'][0] 78 | # Turn 'sticky_actions' into a set of active actions (strongly typed). 79 | obs['sticky_actions'] = { sticky_index_to_action[nr] for nr, action in enumerate(obs['sticky_actions']) if action } 80 | # Turn 'game_mode' into an enum. 81 | obs['game_mode'] = GameMode(obs['game_mode']) 82 | # In case of single agent mode, 'designated' is always equal to 'active'. 83 | if 'designated' in obs: 84 | del obs['designated'] 85 | # Conver players' roles to enum. 86 | obs['left_team_roles'] = [ PlayerRole(role) for role in obs['left_team_roles'] ] 87 | obs['right_team_roles'] = [ PlayerRole(role) for role in obs['right_team_roles'] ] 88 | 89 | action = agent(obs) 90 | return [action.value] 91 | 92 | return agent_wrapper 93 | -------------------------------------------------------------------------------- /kaggle_environments/envs/halite/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/halite/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/hungry_geese/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/hungry_geese/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/hungry_geese/hungry_geese.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hungry_geese", 3 | "title": "Hungry Geese", 4 | "description": "Similar to the classic snake game with multiple players", 5 | "version": "1.0.0", 6 | "agents": [1, 2, 3, 4, 5, 6, 7, 8], 7 | "configuration": { 8 | "episodeSteps": 200, 9 | "actTimeout": 1, 10 | "columns": { 11 | "description": "Horizontal number of cells on the board.", 12 | "type": "integer", 13 | "default": 11, 14 | "minimum": 3 15 | }, 16 | "rows": { 17 | "description": "Vertical number of cells on the board.", 18 | "type": "integer", 19 | "default": 7, 20 | "minimum": 3 21 | }, 22 | "hunger_rate": { 23 | "description": "The number of steps before the goose shrinks a cell.", 24 | "type": "integer", 25 | "minimum": 1, 26 | "default": 40 27 | }, 28 | "min_food": { 29 | "description": "The minimum amount of food present for all steps.", 30 | "type": "integer", 31 | "default": 2, 32 | "minimum": 1 33 | }, 34 | "max_length": { 35 | "description": "The max length any goose can be. Total reward = (max length + 1) * steps survived + goose length.", 36 | "type": "integer", 37 | "default": 99 38 | } 39 | }, 40 | "reward": { 41 | "description": "steps survived * (max goose length + 1) + current goose length.", 42 | "type": "integer", 43 | "default": 0, 44 | "minimum": 0 45 | }, 46 | "observation": { 47 | "geese": { 48 | "description": "List of geese in order by the agent index.", 49 | "type": "array", 50 | "default": [], 51 | "shared": true, 52 | "items": { 53 | "description": "A goose represented by positions on the board.", 54 | "type": "array", 55 | "items": { 56 | "description": "A position of a goose with the 0 position representing the head.", 57 | "type": "integer", 58 | "minimum": 0 59 | } 60 | } 61 | }, 62 | "food": { 63 | "description": "Positions on the board where food is present.", 64 | "type": "array", 65 | "default": [], 66 | "shared": true, 67 | "items": { 68 | "type": "integer", 69 | "minimum": 0 70 | } 71 | }, 72 | "index": { 73 | "description": "Index of the current agent's goose in the list of geese.", 74 | "type": "integer", 75 | "minimum": 0, 76 | "defaults": [0, 1, 2, 3, 4, 5, 6, 7] 77 | }, 78 | "remainingOverageTime": 60 79 | }, 80 | "action": { 81 | "description": "Direction to move the head of your players goose.", 82 | "type": "string", 83 | "enum": ["NORTH", "EAST", "SOUTH", "WEST"], 84 | "default": "NORTH" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /kaggle_environments/envs/hungry_geese/test_hungry_geese.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/hungry_geese/test_hungry_geese.py -------------------------------------------------------------------------------- /kaggle_environments/envs/identity/identity.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "identity", 3 | "title": "Identity", 4 | "description": "Used for debugging. Agent action is set as their reward.", 5 | "version": "1.0.0", 6 | "agents": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 7 | "configuration": { 8 | "max": { 9 | "description": "The highest number allowed for an action.", 10 | "type": "integer", 11 | "default": 1000, 12 | "minimum": 1 13 | }, 14 | "min": { 15 | "description": "The lowest number allowed for an action.", 16 | "type": "integer", 17 | "default": 0, 18 | "minimum": 0 19 | }, 20 | "noise": { 21 | "description": "Noise added to the reward.", 22 | "type": "number", 23 | "default": 0 24 | }, 25 | "actTimeout": 1, 26 | "episodeSteps": 25 27 | }, 28 | "reward": { 29 | "description": "The value of the agent action is set as: action + gauss * noise.", 30 | "type": "integer" 31 | }, 32 | "observation": { 33 | "remainingOverageTime": 12 34 | }, 35 | "action": { 36 | "description": "Reward = action if number, otherwise 0.", 37 | "type": ["number", "string", "null"] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /kaggle_environments/envs/identity/identity.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Kaggle Inc 2 | # 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 | import json 16 | import time 17 | from os import path 18 | from random import choice, gauss 19 | 20 | 21 | def random_agent(obs, config): 22 | return choice(range(config.min, config.max)) 23 | 24 | 25 | def max_agent(obs, config): 26 | return config.max 27 | 28 | 29 | def min_agent(obs, config): 30 | return config.min 31 | 32 | 33 | def avg_agent(obs, config): 34 | return (config.min + config.max) // 2 35 | 36 | 37 | def timeout_agent(obs, config): 38 | time.sleep(config.actTimeout + obs.remainingOverageTime / 2) 39 | return random_agent(obs, config) 40 | 41 | 42 | agents = { 43 | "random": random_agent, 44 | "max": max_agent, 45 | "min": min_agent, 46 | "avg": avg_agent, 47 | "timeout": timeout_agent, 48 | } 49 | 50 | 51 | def interpreter(state, env): 52 | if env.done: 53 | return state 54 | 55 | # Validate and assign actions as rewards !(min <= action <= max). 56 | for agent in state: 57 | value = 0 58 | if isinstance(agent.action, (int, float)): 59 | value = agent.action 60 | if value < env.configuration.min or value > env.configuration.max: 61 | agent.status = f"Invalid action: {value}" 62 | else: 63 | agent.reward += value + \ 64 | gauss(0, 1) * env.configuration.noise // 1 65 | 66 | if len(env.steps) >= env.configuration.episodeSteps: 67 | for agent in state: 68 | agent.status = "DONE" 69 | 70 | return state 71 | 72 | 73 | def renderer(state, env): 74 | return json.dumps([{"action": a.action, "reward": a.reward} for a in state]) 75 | 76 | 77 | def html_renderer(state, env): 78 | return "" 79 | 80 | 81 | dirpath = path.dirname(__file__) 82 | jsonpath = path.abspath(path.join(dirpath, "identity.json")) 83 | with open(jsonpath) as f: 84 | specification = json.load(f) 85 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/kore_fleets/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/kore_fleets.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Kore - Kaggle Environment" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": { 14 | "scrolled": true 15 | }, 16 | "outputs": [], 17 | "source": [ 18 | "!pip install ../../.." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "from kaggle_environments import make\n", 28 | "env = make(\"kore_fleets\")\n", 29 | "print(env.name, env.version)\n", 30 | "print(\"Default Agents: \", *env.agents)" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "## TLDR;" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": { 44 | "scrolled": false 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "env = make(\"kore_fleets\", debug=True)\n", 49 | "env.run([\"simp\", \"simp\", \"simp\", \"simp\"])\n", 50 | "#env.run([\"simp\", \"random\", \"random\", \"random\"])\n", 51 | "#env.run([\"simple\", \"simple\"])\n", 52 | "env.render(mode=\"ipython\", width=970, height=800)" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "## Specification" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": { 66 | "scrolled": false 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "import json\n", 71 | "print(\"Configuration:\", json.dumps(env.specification.configuration, indent=4, sort_keys=True))\n", 72 | "print(\"Observation:\", json.dumps(env.specification.observation, indent=4, sort_keys=True))\n", 73 | "print(\"Action:\", json.dumps(env.specification.action, indent=4, sort_keys=True))" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [] 89 | } 90 | ], 91 | "metadata": { 92 | "kernelspec": { 93 | "display_name": "Python 3", 94 | "language": "python", 95 | "name": "python3" 96 | }, 97 | "language_info": { 98 | "codemirror_mode": { 99 | "name": "ipython", 100 | "version": 3 101 | }, 102 | "file_extension": ".py", 103 | "mimetype": "text/x-python", 104 | "name": "python", 105 | "nbconvert_exporter": "python", 106 | "pygments_lexer": "ipython3", 107 | "version": "3.9.9" 108 | } 109 | }, 110 | "nbformat": 4, 111 | "nbformat_minor": 2 112 | } 113 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/Bot.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | import java.util.Map.Entry; 3 | 4 | import kore.*; 5 | 6 | public class Bot { 7 | private final static Scanner scanner = new Scanner(System.in); 8 | public static void main(final String[] args) throws Exception { 9 | while (true) { 10 | /** Do not edit! **/ 11 | String rawObservation = scanner.nextLine(); 12 | String rawConfiguration = scanner.nextLine(); 13 | Board board = new Board(rawObservation, rawConfiguration); 14 | /** end do not edit */ 15 | 16 | Player me = board.currentPlayer(); 17 | int turn = board.step; 18 | int spawnCost = board.configuration.spawnCost; 19 | double koreLeft = me.kore; 20 | 21 | for (Shipyard shipyard : me.shipyards()) { 22 | if (shipyard.shipCount > 10) { 23 | Direction dir = Direction.fromIndex(turn % 4); 24 | ShipyardAction action = ShipyardAction.launchFleetWithFlightPlan(2, dir.toChar()); 25 | shipyard.setNextAction(action); 26 | } else if (koreLeft > spawnCost * shipyard.maxSpawn()) { 27 | ShipyardAction action = ShipyardAction.spawnShips(shipyard.maxSpawn()); 28 | shipyard.setNextAction(action); 29 | koreLeft -= spawnCost * shipyard.maxSpawn(); 30 | } else if (koreLeft > spawnCost) { 31 | ShipyardAction action = ShipyardAction.spawnShips(1); 32 | shipyard.setNextAction(action); 33 | koreLeft -= spawnCost; 34 | } 35 | } 36 | 37 | /** AI Code Goes Above! **/ 38 | 39 | /** Do not edit! **/ 40 | StringBuilder commandBuilder = new StringBuilder(""); 41 | boolean first = true; 42 | for (Entry entry : board.currentPlayer().nextActions().entrySet()) { 43 | if (first) { 44 | first = false; 45 | } else { 46 | commandBuilder.append(","); 47 | } 48 | commandBuilder.append(String.format("%s:%s", entry.getKey(), entry.getValue().toString())); 49 | } 50 | System.out.println(commandBuilder.toString()); 51 | System.out.flush(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/README.md: -------------------------------------------------------------------------------- 1 | # Kore Fleets Java Bot 2 | 3 | ## Running locally 4 | 5 | 1. compile your bot with `javac Bot.java` 6 | 2. run a match with `kaggle-environments run --environment kore_fleets --agents ./main.py ./main.py` 7 | 8 | * where `./main.py` is the relative path from your current working directory to your bot's `main.py` file 9 | * 1, 2, or 4 agents are supported 10 | ## Watching a replay locally 11 | 1. compile your bot with `javac Bot.java` 12 | 2. run a match with `kaggle-environments run --environment kore_fleets --agents ./main.py ./main.py --log out.log --out replay.html --render '{"mode": "html"}'` 13 | 3. open `replay.html` with your browser of choice! 14 | 15 | 16 | ## Creating a submission 17 | 18 | 1. compile your both with `javac Bot.java` 19 | 2. create a tar.gz with `tar --exclude='test' --exclude='jars' -czvf submission.tar.gz *` 20 | 21 | note you must do this in the same directory as `main.py`! 22 | 23 | ## Running tests 24 | 25 | 1. Add the jars/ to visual studio as described [here](https://stackoverflow.com/questions/50232557/visual-studio-code-java-extension-howto-add-jar-to-classpath) 26 | 2. Run tests! 27 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/jars/hamcrest-core-1.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/kore_fleets/starter_bots/java/jars/hamcrest-core-1.3.jar -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/jars/junit-4.13.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/kore_fleets/starter_bots/java/jars/junit-4.13.2.jar -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Cell.java: -------------------------------------------------------------------------------- 1 | package kore; 2 | 3 | import java.util.Optional; 4 | 5 | public class Cell { 6 | 7 | public final Point position; 8 | public double kore; 9 | public String shipyardId; 10 | public String fleetId; 11 | public final Board board; 12 | 13 | public Cell(Point position, double kore, String shipyardId, String fleetId, Board board) { 14 | this.position = position; 15 | this.kore = kore; 16 | this.shipyardId = shipyardId; 17 | this.fleetId = fleetId; 18 | this.board = board; 19 | } 20 | 21 | public Cell cloneToBoard(Board board) { 22 | return new Cell(this.position, this.kore, this.shipyardId, this.fleetId, board); 23 | } 24 | 25 | public Optional fleet() { 26 | if (this.board.fleets.containsKey(this.fleetId)) { 27 | return Optional.of(this.board.fleets.get(this.fleetId)); 28 | } 29 | return Optional.empty(); 30 | } 31 | 32 | 33 | public Optional shipyard() { 34 | if (this.board.shipyards.containsKey(this.shipyardId)) { 35 | return Optional.of(this.board.shipyards.get(this.shipyardId)); 36 | } 37 | return Optional.empty(); 38 | } 39 | 40 | public Cell neighbor(Point offset) { 41 | Point next = this.position.translate(offset, this.board.size); 42 | return this.board.getCellAtPosition(next); 43 | } 44 | 45 | 46 | public Cell north() { 47 | return this.neighbor(Direction.NORTH); 48 | } 49 | 50 | public Cell south() { 51 | return this.neighbor(((Point)Direction.SOUTH)); 52 | } 53 | 54 | public Cell east() { 55 | return this.neighbor(Direction.EAST); 56 | } 57 | 58 | public Cell west() { 59 | return this.neighbor(Direction.WEST); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Configuration.java: -------------------------------------------------------------------------------- 1 | package kore; 2 | 3 | public class Configuration { 4 | 5 | public final int agentTimeout; 6 | public final int startingKore; 7 | public final int size; 8 | public final double spawnCost; 9 | public final int convertCost; 10 | public final double regenRate; 11 | public final int maxRegenCellKore; 12 | public final int randomSeed; 13 | 14 | public Configuration(String rawConfiguration) { 15 | this.agentTimeout = KoreJson.getIntFromJson(rawConfiguration, "agentTimeout"); 16 | this.startingKore = KoreJson.getIntFromJson(rawConfiguration, "startingKore"); 17 | this.size = KoreJson.getIntFromJson(rawConfiguration, "size"); 18 | this.spawnCost = KoreJson.getDoubleFromJson(rawConfiguration, "spawnCost"); 19 | this.convertCost = KoreJson.getIntFromJson(rawConfiguration, "convertCost"); 20 | this.regenRate = KoreJson.getDoubleFromJson(rawConfiguration, "regenRate"); 21 | this.maxRegenCellKore = KoreJson.getIntFromJson(rawConfiguration, "maxRegenCellKore"); 22 | this.randomSeed = KoreJson.getIntFromJson(rawConfiguration, "randomSeed"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Fleet.java: -------------------------------------------------------------------------------- 1 | package kore; 2 | 3 | public class Fleet { 4 | 5 | public final String id; 6 | public int shipCount; 7 | public Direction direction; 8 | public Point position; 9 | public String flightPlan; 10 | public double kore; 11 | public final int playerId; 12 | public final Board board; 13 | 14 | public Fleet(String fleetId, int shipCount, Direction direction, Point position, double kore, String flightPlan, int playerId, Board board) { 15 | this.id = fleetId; 16 | this.shipCount = shipCount; 17 | this.direction = direction; 18 | this.position = position; 19 | this.flightPlan = flightPlan; 20 | this.kore = kore; 21 | this.playerId = playerId; 22 | this.board = board; 23 | } 24 | 25 | public Fleet cloneToBoard(Board board) { 26 | return new Fleet(this.id, this.shipCount, this.direction, this.position, this.kore, this.flightPlan, this.playerId, board); 27 | } 28 | 29 | public Cell cell() { 30 | return this.board.getCellAtPosition(this.position); 31 | } 32 | 33 | public Player player() { 34 | return this.board.players[this.playerId]; 35 | } 36 | 37 | public double collectionRate() { 38 | return Math.min(Math.log(this.shipCount) / 10, .99); 39 | } 40 | 41 | /** 42 | * Returns the length of the longest possible flight plan this fleet can be assigned 43 | * @return 44 | */ 45 | public static int maxFlightPlanLenForShipCount(int shipCount) { 46 | return (int) (Math.floor(2 * Math.log(shipCount)) + 1); 47 | } 48 | 49 | /** 50 | * Converts a fleet back to the normalized observation subset that constructed it. 51 | */ 52 | public String[] observation() { 53 | return new String[]{ 54 | String.valueOf(this.position.toIndex(this.board.configuration.size)), 55 | String.valueOf(this.kore), 56 | String.valueOf(this.shipCount), 57 | String.valueOf(this.direction.toIndex()), 58 | this.flightPlan 59 | }; 60 | } 61 | 62 | public boolean lessThanOtherAlliedFleet(Fleet other) { 63 | if (this.shipCount != other.shipCount) { 64 | return this.shipCount < other.shipCount; 65 | } 66 | if (this.kore != other.kore) { 67 | return this.kore < other.kore; 68 | } 69 | return this.direction.toIndex() > other.direction.toIndex(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Observation.java: -------------------------------------------------------------------------------- 1 | package kore; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | 7 | public class Observation { 8 | public final double[] kore; 9 | public final double[] playerHlt; 10 | public final ArrayList> playerShipyards; 11 | public final ArrayList> playerFleets; 12 | public final int player; 13 | public final int step; 14 | public final double remainingOverageTime; 15 | 16 | private static String shortenFrontAndBack(String target, int n) { 17 | return target.substring(n, target.length() - n); 18 | } 19 | 20 | public Observation(String rawObservation) { 21 | // avoid importing json library? worth it? 22 | this.kore = KoreJson.getDoubleArrFromJson(rawObservation, "kore"); 23 | this.player = KoreJson.getPlayerIdxFromJson(rawObservation); 24 | this.step = KoreJson.getIntFromJson(rawObservation, "step"); 25 | this.remainingOverageTime = KoreJson.getDoubleFromJson(rawObservation, "remainingOverageTime"); 26 | String[] playerParts = KoreJson.getPlayerPartsFromJson(rawObservation); 27 | playerHlt = new double[playerParts.length]; 28 | playerShipyards = new ArrayList>(); 29 | playerFleets = new ArrayList>(); 30 | 31 | for (int i = 0; i < playerParts.length; i ++) { 32 | String playerPart = playerParts[i]; 33 | playerHlt[i] = Double.parseDouble(playerPart.split(", ")[0]); 34 | 35 | int startShipyards = playerPart.indexOf("{"); 36 | int endShipyards = playerPart.indexOf("}"); 37 | String shipyardsStr = playerPart.substring(startShipyards + 1, endShipyards - 1); 38 | HashMap shipyards = new HashMap(); 39 | Arrays.stream(shipyardsStr.split("], ")).forEach(shipyardStr -> { 40 | if (shipyardStr.length() == 0) { 41 | return; 42 | } 43 | String[] kvparts = shipyardStr.split(": \\["); 44 | String shipyardId = shortenFrontAndBack(kvparts[0], 1); 45 | String[] shipyardStrs = kvparts[1].split(", "); 46 | int[] shipyard = new int[shipyardStrs.length]; 47 | Integer[] shipyardInts = Arrays.stream(shipyardStrs).map(s -> Integer.parseInt(s)).toArray(Integer[]::new); 48 | for(int j = 0; j < shipyard.length; j++) { 49 | shipyard[j] = shipyardInts[j]; 50 | } 51 | shipyards.put(shipyardId, shipyard); 52 | }); 53 | playerShipyards.add(shipyards); 54 | 55 | int startFleets = playerPart.indexOf("}, "); 56 | String fleetsStr = playerPart.substring(startFleets + 4, playerPart.length() - 1); 57 | HashMap fleets = new HashMap<>(); 58 | Arrays.stream(fleetsStr.split("], ")).forEach(fleetStr -> { 59 | if (fleetStr.length() == 0) { 60 | return; 61 | } 62 | String[] kvparts = fleetStr.split(": "); 63 | String fleetId = shortenFrontAndBack(kvparts[0], 1); 64 | String[] fleet = shortenFrontAndBack(kvparts[1], 1).split(", "); 65 | fleets.put(fleetId, fleet); 66 | }); 67 | playerFleets.add(fleets); 68 | } 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Pair.java: -------------------------------------------------------------------------------- 1 | package kore; 2 | 3 | public class Pair { 4 | 5 | public final F first; 6 | public final S second; 7 | 8 | public Pair(F first, S second) { 9 | this.first = first; 10 | this.second = second; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Player.java: -------------------------------------------------------------------------------- 1 | package kore; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.stream.Collectors; 7 | 8 | public class Player { 9 | public final int id; 10 | public double kore; 11 | public final ArrayList shipyardIds; 12 | public final ArrayList fleetIds; 13 | public final Board board; 14 | 15 | public Player(int playerId, double kore, ArrayList shipyardIds, ArrayList fleetIds, Board board) { 16 | this.id = playerId; 17 | this.kore = kore; 18 | this.shipyardIds = shipyardIds; 19 | this.fleetIds = fleetIds; 20 | this.board = board; 21 | } 22 | 23 | public Player cloneToBoard(Board board) { 24 | return new Player(this.id, this.kore, new ArrayList(this.shipyardIds.stream().collect(Collectors.toList())), new ArrayList(this.fleetIds.stream().collect(Collectors.toList())), board); 25 | } 26 | 27 | /** 28 | * Returns all shipyards owned by this player. 29 | * @return 30 | */ 31 | public Shipyard[] shipyards() { 32 | return this.board.shipyards.values().stream().filter(shipyard -> this.shipyardIds.stream().anyMatch(sId -> sId == shipyard.id)).toArray(Shipyard[]::new); 33 | } 34 | 35 | /** 36 | * Returns all fleets owned by this player. 37 | */ 38 | public Fleet[] fleets() { 39 | return this.board.fleets.values().stream().filter(fleet -> this.fleetIds.stream().anyMatch(fId -> fId == fleet.id)).toArray(Fleet[]::new); 40 | } 41 | 42 | /** 43 | * Returns whether this player is the current player (generally if this returns True, this player is you. 44 | */ 45 | public boolean isCurrentPlayer() { 46 | return this.id == this.board.currentPlayerId; 47 | } 48 | 49 | /** 50 | * Returns all queued fleet and shipyard actions for this player formatted for the kore interpreter to receive as an agent response. 51 | */ 52 | public HashMap nextActions() { 53 | HashMap result = new HashMap<>(); 54 | Arrays.stream(this.shipyards()).filter(shipyard -> shipyard.nextAction.isPresent()).forEach(shipyard -> result.put(shipyard.id, shipyard.nextAction.get())); 55 | return result; 56 | } 57 | 58 | /** 59 | * Converts a player back to the normalized observation subset that constructed it. 60 | */ 61 | public Object[] observation() { 62 | HashMap shipyards = new HashMap<>(); 63 | Arrays.stream(this.shipyards()).forEach(shipyard -> shipyards.put(shipyard.id, shipyard.observation())); 64 | HashMap fleets = new HashMap<>(); 65 | Arrays.stream(this.fleets()).forEach(fleet -> fleets.put(fleet.id, fleet.observation())); 66 | return new Object[]{this.kore, shipyards, fleets}; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Point.java: -------------------------------------------------------------------------------- 1 | package kore; 2 | 3 | public class Point { 4 | public final int x, y; 5 | 6 | public Point(int x, int y) { 7 | this.x = x; 8 | this.y = y; 9 | } 10 | 11 | public Point translate(Point offset, int size) { 12 | return this.add(offset).mod(size); 13 | } 14 | 15 | public Point add(Point other) { 16 | return new Point(this.x + other.x, this.y + other.y); 17 | } 18 | 19 | public Point mod(int size) { 20 | return new Point(this.x % size, this.y % size); 21 | } 22 | 23 | /** 24 | * Gets the manhatten distance between two points 25 | */ 26 | public int distanceTo(Point other, int size) { 27 | int abs_x = Math.abs(this.x - other.x); 28 | int dist_x = abs_x < size/2 ? abs_x : size - abs_x; 29 | int abs_y = Math.abs(this.y - other.y); 30 | int dist_y = abs_y < size/2 ? abs_y : size - abs_y; 31 | return dist_x + dist_y; 32 | } 33 | 34 | /** 35 | * Converts a 2d position in the form (x, y) to an index in the observation.kore list. 36 | * See fromIndex for the inverse. 37 | */ 38 | public int toIndex(int size) { 39 | return (size - this.y - 1) * size + this.x; 40 | } 41 | 42 | public static Point fromIndex(int index, int size) { 43 | return new Point(index % size, size - index/size - 1); 44 | } 45 | 46 | public Point abs() { 47 | return new Point(Math.abs(this.x), Math.abs(this.y)); 48 | } 49 | 50 | public boolean equals(Point other) { 51 | return this.x == other.x && this.y == other.y; 52 | } 53 | 54 | public String toString() { 55 | return "(" + this.x + "," + this.y + ")"; 56 | } 57 | 58 | public Point multiply(int factor) { 59 | return new Point(factor * this.x, factor * this.y); 60 | } 61 | 62 | public Point subtract(Point other) { 63 | return new Point(this.x - other.x, this.y - other.y); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Shipyard.java: -------------------------------------------------------------------------------- 1 | package kore; 2 | 3 | import java.util.Optional; 4 | 5 | public class Shipyard { 6 | public final String id; 7 | public int shipCount; 8 | public Point position; 9 | public int playerId; 10 | public int turnsControlled; 11 | public final Board board; 12 | public Optional nextAction; 13 | private final int[] SPAWN_VALUES; 14 | 15 | public Shipyard(String shipyardId, int shipCount, Point position, int playerId, int turnsControlled, Board board, Optional nextAction) { 16 | this.id = shipyardId; 17 | this.shipCount = shipCount; 18 | this.position = position; 19 | this.playerId = playerId; 20 | this.turnsControlled = turnsControlled; 21 | this.board = board; 22 | this.nextAction = nextAction; 23 | 24 | int[] upgradeTimes = new int[9]; 25 | for(int i = 1; i < 10; i++) { 26 | upgradeTimes[i-1] = (int) Math.pow(i, 2) + 1; 27 | } 28 | SPAWN_VALUES = new int[9]; 29 | int current = 0; 30 | for(int i = 1; i < 10; i++) { 31 | current += upgradeTimes[i-1]; 32 | SPAWN_VALUES[i-1] = current; 33 | } 34 | } 35 | 36 | public Shipyard cloneToBoard(Board board) { 37 | return new Shipyard(this.id, this.shipCount, this.position, this.playerId, this.turnsControlled, board, this.nextAction); 38 | } 39 | 40 | public void setNextAction(ShipyardAction action) { 41 | this.nextAction = Optional.of(action); 42 | } 43 | 44 | public int maxSpawn() { 45 | for (int i = 0; i < this.SPAWN_VALUES.length; i++) { 46 | if (this.turnsControlled < this.SPAWN_VALUES[i]) { 47 | return i + 1; 48 | } 49 | } 50 | return this.SPAWN_VALUES.length + 1; 51 | } 52 | 53 | /** 54 | * Returns the cell this shipyard is on. 55 | */ 56 | public Cell cell() { 57 | return this.board.getCellAtPosition(this.position); 58 | } 59 | 60 | public Player player() { 61 | return this.board.players[this.playerId]; 62 | } 63 | 64 | /** 65 | * Converts a shipyard back to the normalized observation subset that constructed it. 66 | */ 67 | public int[] observation() { 68 | return new int[]{this.position.toIndex(this.board.configuration.size), this.shipCount, this.turnsControlled}; 69 | } 70 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/kore/ShipyardAction.java: -------------------------------------------------------------------------------- 1 | package kore; 2 | 3 | public class ShipyardAction { 4 | public static final String SPAWN = "SPAWN"; 5 | public static final String LAUNCH = "LAUNCH"; 6 | public final String actionType; 7 | public final int shipCount; 8 | public final String flightPlan; 9 | 10 | public static ShipyardAction spawnShips(int shipCount) { 11 | return new ShipyardAction(SPAWN, shipCount, ""); 12 | } 13 | 14 | public static ShipyardAction launchFleetWithFlightPlan(int shipCount, String flightPlan) { 15 | return new ShipyardAction(LAUNCH, shipCount, flightPlan); 16 | } 17 | 18 | public static ShipyardAction fromString(String raw) { 19 | if (raw.length() == 0) { 20 | throw new IllegalStateException("invalid raw shipyard empty string"); 21 | } 22 | int shipCount = Integer.parseInt(raw.split("_")[1]); 23 | if (raw.startsWith(LAUNCH)) { 24 | return ShipyardAction.spawnShips(shipCount); 25 | } 26 | if (raw.startsWith(SPAWN)) { 27 | String flightPlan = raw.split("_")[2]; 28 | return ShipyardAction.launchFleetWithFlightPlan(shipCount, flightPlan); 29 | } 30 | throw new IllegalStateException("invalid Shipyard Action raw " + raw); 31 | } 32 | 33 | public ShipyardAction(String type, int shipCount, String flightPlan) { 34 | assert type.equals(SPAWN) || type.equals(LAUNCH) : "Type must be SPAWN or LAUNCH"; 35 | assert shipCount >= 0 : "numShips must be a non-negative number"; 36 | this.actionType = type; 37 | this.shipCount = shipCount; 38 | this.flightPlan = flightPlan; 39 | } 40 | 41 | private boolean isSpawn() { 42 | return this.actionType.equals(SPAWN); 43 | } 44 | 45 | private boolean isLaunch() { 46 | return this.actionType.equals(LAUNCH); 47 | } 48 | 49 | public String toString() { 50 | if (this.isSpawn()) { 51 | return String.format("%s_%d", SPAWN, this.shipCount); 52 | } 53 | if (this.isLaunch()) { 54 | return String.format("%s_%d_%s", LAUNCH, this.shipCount, this.flightPlan); 55 | } 56 | throw new IllegalStateException("invalid Shpyard Action"); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/main.py: -------------------------------------------------------------------------------- 1 | from subprocess import Popen, PIPE 2 | from threading import Thread 3 | from queue import Queue, Empty 4 | 5 | import atexit 6 | import os 7 | import sys 8 | agent_processes = [None, None, None, None] 9 | t = None 10 | q = None 11 | def cleanup_process(): 12 | global agent_processes 13 | for proc in agent_processes: 14 | if proc is not None: 15 | proc.kill() 16 | def enqueue_output(out, queue): 17 | for line in iter(out.readline, b''): 18 | queue.put(line) 19 | out.close() 20 | 21 | def agent(observation, configuration): 22 | global agent_processes, t, q 23 | 24 | agent_process = agent_processes[observation.player] 25 | ### Do not edit ### 26 | if agent_process is None: 27 | if "__raw_path__" in configuration: 28 | cwd = os.path.dirname(configuration["__raw_path__"]) 29 | else: 30 | cwd = os.path.dirname(__file__) 31 | agent_process = Popen(['java', 'Bot'], stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=cwd) 32 | agent_processes[observation.player] = agent_process 33 | atexit.register(cleanup_process) 34 | 35 | # following 4 lines from https://stackoverflow.com/questions/375427/a-non-blocking-read-on-a-subprocess-pipe-in-python 36 | q = Queue() 37 | t = Thread(target=enqueue_output, args=(agent_process.stderr, q)) 38 | t.daemon = True # thread dies with the program 39 | t.start() 40 | 41 | # print observations to agent 42 | import json 43 | agent_process.stdin.write((json.dumps(observation) + "\n").encode()) 44 | agent_process.stdin.write((json.dumps(configuration) + "\n").encode()) 45 | agent_process.stdin.flush() 46 | 47 | # wait for data written to stdout 48 | agent1res = (agent_process.stdout.readline()).decode() 49 | 50 | while True: 51 | try: line = q.get_nowait() 52 | except Empty: 53 | # no standard error received, break 54 | break 55 | else: 56 | # standard error output received, print it out 57 | print(line.decode(), file=sys.stderr, end='') 58 | 59 | agent1res = agent1res.strip() 60 | outputs = agent1res.split(",") 61 | actions = {} 62 | for cmd in outputs: 63 | if cmd != "": 64 | shipyard_id, action_str = cmd.split(":") 65 | actions[shipyard_id] = action_str 66 | return actions 67 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/test/ConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | import kore.Configuration; 12 | 13 | public class ConfigurationTest { 14 | 15 | @Test 16 | public void givenValidConfiguration_createSuccessful() throws IOException { 17 | Path configPath = Paths.get("bin", "test", "configuration.json"); 18 | String rawConfig = Files.readString(configPath); 19 | 20 | Configuration config = new Configuration(rawConfig); 21 | 22 | Assert.assertEquals(0.02, config.regenRate, .001); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/test/KoreJsonTest.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | import kore.KoreJson; 12 | 13 | public class KoreJsonTest { 14 | @Test 15 | public void containsKey() throws IOException { 16 | Path observation = Paths.get("bin", "test", "observation.json"); 17 | String raw = Files.readString(observation); 18 | 19 | Assert.assertTrue(KoreJson.containsKey(raw, "kore")); 20 | Assert.assertFalse(KoreJson.containsKey(raw, "notThere")); 21 | } 22 | 23 | @Test 24 | public void getIntFromJson() throws IOException { 25 | Path observation = Paths.get("bin", "test", "observation.json"); 26 | String raw = Files.readString(observation); 27 | 28 | Assert.assertEquals(KoreJson.getIntFromJson(raw, "step"), 16); 29 | } 30 | 31 | @Test 32 | public void getStrFromJson() { 33 | Assert.assertTrue(KoreJson.getStrFromJson("{'test': 'foo'}", "test").equals("foo")); 34 | } 35 | 36 | @Test 37 | public void getFloatArrFromJson() throws IOException { 38 | Path observation = Paths.get("bin", "test", "observation.json"); 39 | String raw = Files.readString(observation); 40 | 41 | double[] kore = KoreJson.getDoubleArrFromJson(raw, "kore"); 42 | Assert.assertEquals(kore[3], 1.372, 0.0001); 43 | } 44 | 45 | @Test 46 | public void getPlayerPartsFromJson() throws IOException { 47 | Path observation = Paths.get("bin", "test", "observation.json"); 48 | String raw = Files.readString(observation); 49 | 50 | String[] players = KoreJson.getPlayerPartsFromJson(raw); 51 | Assert.assertEquals(players.length, 4); 52 | Assert.assertEquals(players[0].substring(0, 3), "500"); 53 | } 54 | 55 | @Test 56 | public void getPlayerIdxFromJson() throws IOException { 57 | Path observation = Paths.get("bin", "test", "observation.json"); 58 | String raw = Files.readString(observation); 59 | 60 | Assert.assertEquals(KoreJson.getPlayerIdxFromJson(raw), 0); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/test/ObservationTest.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | import kore.Observation; 12 | 13 | public class ObservationTest { 14 | 15 | @Test 16 | public void givenValidObservation_createSuccessful() throws IOException { 17 | Path observation = Paths.get("bin", "test", "observation.json"); 18 | String rawObservation = Files.readString(observation); 19 | 20 | Observation ob = new Observation(rawObservation); 21 | 22 | Assert.assertEquals(0, ob.player); 23 | Assert.assertEquals(16, ob.step); 24 | Assert.assertEquals(4, ob.playerHlt.length); 25 | Assert.assertEquals(4, ob.playerFleets.size()); 26 | Assert.assertEquals(0, ob.playerFleets.get(0).size()); 27 | Assert.assertEquals(4, ob.playerShipyards.size()); 28 | Assert.assertEquals(1, ob.playerShipyards.get(0).size()); 29 | } 30 | 31 | @Test 32 | public void givenFullObservation_createSuccessful() throws IOException { 33 | Path observation = Paths.get("bin", "test", "fullob.json"); 34 | String rawObservation = Files.readString(observation); 35 | 36 | Observation ob = new Observation(rawObservation); 37 | 38 | Assert.assertEquals(0, ob.player); 39 | Assert.assertEquals(200, ob.step); 40 | Assert.assertEquals(2, ob.playerHlt.length); 41 | Assert.assertEquals(2, ob.playerFleets.size()); 42 | Assert.assertEquals(1, ob.playerFleets.get(0).size()); 43 | Assert.assertEquals(2, ob.playerShipyards.size()); 44 | Assert.assertEquals(6, ob.playerShipyards.get(0).size()); 45 | } 46 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/test/PointTest.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import kore.Point; 7 | 8 | public class PointTest { 9 | 10 | @Test 11 | public void fromIndexToIndex_isIdentity() { 12 | int idx = 254; 13 | int size = 31; 14 | 15 | Point point = Point.fromIndex(idx, size); 16 | int mirroredIdx = point.toIndex(size); 17 | 18 | Assert.assertEquals(idx, mirroredIdx); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/test/ShipyardTest.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import kore.Point; 7 | import kore.Shipyard; 8 | 9 | public class ShipyardTest { 10 | 11 | @Test 12 | public void maxSpawn_worksCorrectly() { 13 | int[] turns = {0, 1, 2, 293, 294, 295}; 14 | int[] expected = {1, 1, 2, 9, 10, 10}; 15 | for (int i = 0; i < turns.length; i ++) { 16 | Shipyard shipyard = new Shipyard("A", 0, new Point(0, 0), 1, turns[i], null, null); 17 | 18 | Assert.assertEquals(shipyard.maxSpawn(), expected[i]); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/java/test/configuration.json: -------------------------------------------------------------------------------- 1 | {"episodeSteps": 400, "actTimeout": 3, "runTimeout": 9600, "startingKore": 6000, "size": 31, "spawnCost": 10, "convertCost": 50, "regenRate": 0.02, "maxRegenCellKore": 500, "agentTimeout": 60, "randomSeed": 1464814655, "__raw_path__": "java/main.py"} 2 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/python/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/kore_fleets/starter_bots/python/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/python/main.py: -------------------------------------------------------------------------------- 1 | from kaggle_environments.envs.kore_fleets.helpers import * 2 | from random import randint 3 | 4 | def agent(obs, config): 5 | board = Board(obs, config) 6 | me=board.current_player 7 | 8 | me = board.current_player 9 | turn = board.step 10 | spawn_cost = board.configuration.spawn_cost 11 | kore_left = me.kore 12 | 13 | for shipyard in me.shipyards: 14 | if shipyard.ship_count > 10: 15 | direction = Direction.from_index(turn % 4) 16 | action = ShipyardAction.launch_fleet_with_flight_plan(2, direction.to_char()) 17 | shipyard.next_action = action 18 | elif kore_left > spawn_cost * shipyard.max_spawn: 19 | action = ShipyardAction.spawn_ships(shipyard.max_spawn) 20 | shipyard.next_action = action 21 | kore_left -= spawn_cost * shipyard.max_spawn 22 | elif kore_left > spawn_cost: 23 | action = ShipyardAction.spawn_ships(1) 24 | shipyard.next_action = action 25 | kore_left -= spawn_cost 26 | 27 | return me.next_actions 28 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/Bot.ts: -------------------------------------------------------------------------------- 1 | import {Board} from "./kore/Board"; 2 | import { Direction } from "./kore/Direction"; 3 | import { ShipyardAction } from "./kore/ShipyardAction"; 4 | import { KoreIO } from "./kore/KoreIO"; 5 | 6 | export const tick = async (board: Board): Promise => { 7 | const me = board.currentPlayer; 8 | const turn = board.step; 9 | const spawnCost = board.configuration.spawnCost; 10 | let koreLeft = me.kore; 11 | 12 | for (let shipyard of me.shipyards) { 13 | if (shipyard.shipCount > 10) { 14 | const dir = Direction.fromIndex(turn % 4); 15 | const action = ShipyardAction.launchFleetWithFlightPlan(2, dir.toChar()); 16 | shipyard.setNextAction(action); 17 | } else if (koreLeft > spawnCost * shipyard.maxSpawn) { 18 | const action = ShipyardAction.spawnShips(shipyard.maxSpawn); 19 | shipyard.setNextAction(action); 20 | koreLeft -= spawnCost * shipyard.maxSpawn; 21 | } else if (koreLeft > spawnCost) { 22 | const action = ShipyardAction.spawnShips(1); 23 | shipyard.setNextAction(action); 24 | koreLeft -= spawnCost; 25 | } 26 | } 27 | 28 | // nextActions will be pulled off of your shipyards 29 | return board; 30 | } 31 | 32 | // agent.run takes care of running your code per tick 33 | const io = new KoreIO(); 34 | io.run(tick); 35 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/DoNothingBot.ts: -------------------------------------------------------------------------------- 1 | import {Board} from "./kore/Board"; 2 | import { Direction } from "./kore/Direction"; 3 | import { ShipyardAction } from "./kore/ShipyardAction"; 4 | import { KoreIO } from "./kore/KoreIO"; 5 | 6 | 7 | export const tick = async (board: Board): Promise => { 8 | return board; 9 | } 10 | 11 | const io = new KoreIO(); 12 | io.run(tick); 13 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/MinerBot.ts: -------------------------------------------------------------------------------- 1 | import {Board} from "./kore/Board"; 2 | import { Direction } from "./kore/Direction"; 3 | import { ShipyardAction } from "./kore/ShipyardAction"; 4 | import { KoreIO } from "./kore/KoreIO"; 5 | 6 | export const tick = async (board: Board): Promise => { 7 | const me = board.currentPlayer; 8 | const spawnCost = board.configuration.spawnCost; 9 | const convertCost = board.configuration.convertCost; 10 | let remainingKore = me.kore; 11 | 12 | // the following code is mostly auto-generated using GitHub co-pilot 13 | // using miner Python code and instruction "convert python into javascript" as comment prompts 14 | for (let shipyard of me.shipyards) { 15 | if(remainingKore > 1000 && shipyard.maxSpawn > 5) { 16 | if(shipyard.shipCount >= convertCost + 10) { 17 | const gap1 = getRandomInt(3, 9); 18 | const gap2 = getRandomInt(3, 9); 19 | const startDir = Math.floor(Math.random() * 4); 20 | let flightPlan = Direction.listDirections()[startDir].toChar() + gap1; 21 | const nextDir = (startDir + 1) % 4; 22 | flightPlan += Direction.listDirections()[nextDir].toChar() + gap2; 23 | const nextDir2 = (nextDir + 1) % 4; 24 | flightPlan += Direction.listDirections()[nextDir2].toChar(); 25 | shipyard.setNextAction(ShipyardAction.launchFleetWithFlightPlan(Math.min(convertCost + 10, Math.floor(shipyard.shipCount / 2)), flightPlan)); 26 | } else if(remainingKore >= spawnCost) { 27 | remainingKore -= spawnCost; 28 | shipyard.setNextAction(ShipyardAction.spawnShips(Math.min(shipyard.maxSpawn, Math.floor(remainingKore / spawnCost)))); 29 | } 30 | } else if(shipyard.shipCount >= 21) { 31 | const gap1 = getRandomInt(3, 9); 32 | const gap2 = getRandomInt(3, 9); 33 | const startDir = Math.floor(Math.random() * 4); 34 | let flightPlan = Direction.listDirections()[startDir].toChar() + gap1; 35 | const nextDir = (startDir + 1) % 4; 36 | flightPlan += Direction.listDirections()[nextDir].toChar() + gap2; 37 | const nextDir2 = (nextDir + 1) % 4; 38 | flightPlan += Direction.listDirections()[nextDir2].toChar() + gap1; 39 | const nextDir3 = (nextDir2 + 1) % 4; 40 | flightPlan += Direction.listDirections()[nextDir3].toChar(); 41 | shipyard.setNextAction(ShipyardAction.launchFleetWithFlightPlan(21, flightPlan)); 42 | } else if(remainingKore > board.configuration.spawnCost * shipyard.maxSpawn) { 43 | remainingKore -= board.configuration.spawnCost; 44 | if(remainingKore >= spawnCost) { 45 | shipyard.setNextAction(ShipyardAction.spawnShips(Math.min(shipyard.maxSpawn, Math.floor(remainingKore / spawnCost)))); 46 | } 47 | } else if(shipyard.shipCount >= 2) { 48 | const dirStr = Direction.randomDirection().toChar(); 49 | shipyard.setNextAction(ShipyardAction.launchFleetWithFlightPlan(2, dirStr)); 50 | } 51 | } 52 | 53 | // nextActions will be pulled off of your shipyards 54 | return board; 55 | } 56 | 57 | const io = new KoreIO(); 58 | io.run(tick); 59 | 60 | function getRandomInt(min, max) { 61 | return Math.floor(Math.random() * (max - min + 1)) + min; 62 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/README.md: -------------------------------------------------------------------------------- 1 | # Kore Fleets typescript bot 2 | 3 | ## Requirements 4 | 5 | 1. node and npm/npx/yarn 6 | 2. typescript installed 7 | 3. python3 installed 8 | 4. kaggle_environments pip package installed 9 | 10 | ## Getting started 11 | 12 | 1. `npm install` 13 | 14 | ## Running Locally 15 | 16 | 1. transpile your bot with `npm run compile` or `tsc` 17 | 2. run a match with `npm watch4` 18 | 19 | * if you don't have google-chrome installed, open the replay.html in the browser of your choice 20 | * see package.json for more options 21 | 22 | ## Creating a submission 23 | 24 | 1. `npm run package` 25 | 2. upload submission.tar.gz to kaggle 26 | 3. profit! 27 | 28 | ## Running tests 29 | 30 | 1. `npm test` 31 | 32 | ## Interpreter and training 33 | 34 | A basic TS interpreter has been created in `interpreter.ts`. You can use or modify this file to train machine learning models in JS/TS. 35 | 36 | Currently it supports 2 agents and customizable number of episodes. 37 | 38 | It has two modes: `run` and `step`. 39 | 40 | `run` mode: After each episode, you can access the complete history of the game. For each turn, you can access the full observation (state) as a Board object, actions performed and the reward obtained after performing the action. This mode is useful for evaluating an agent. 41 | 42 | `step` mode: The interpreter initializes new games and allows stepping through the game interactively. You have complete control over the board and the agent during each step. This mode is useful for training machine learning models. 43 | 44 | Sample command to run the interpreter can be found in npm scripts as `npm run interpreter:run` and `npm run interpreter:step`. 45 | 46 | ## Miner bot and Do nothing bot 47 | 48 | A sample miner bot `MinerBot.ts` is provided, with Python entrypoint as `miner.py`. It has the same logic as the Python `miner` bot in `kore_fleets.py`. 49 | 50 | To run it aginst Python miner bot with TS interpreter for 20 episodes: 51 | 52 | 1. `npm run compile` 53 | 2. `node --require ts-node/register interpreter.ts 20 ./miner.py miner` 54 | 55 | A sample do nothing bot `DoNothingBot.ts` is also provided. 56 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Cell.ts: -------------------------------------------------------------------------------- 1 | import {Board} from "./Board"; 2 | import {Direction} from "./Direction"; 3 | import {Fleet} from "./Fleet"; 4 | import {Point} from "./Point"; 5 | import {Shipyard} from "./Shipyard"; 6 | 7 | export class Cell { 8 | 9 | public readonly position: Point; 10 | public kore: number; 11 | public shipyardId: string; 12 | public fleetId: string; 13 | public readonly board: Board; 14 | 15 | public constructor(position: Point, kore: number, shipyardId: string, fleetId: string, board: Board) { 16 | this.position = position; 17 | this.kore = kore; 18 | this.shipyardId = shipyardId; 19 | this.fleetId = fleetId; 20 | this.board = board; 21 | } 22 | 23 | public cloneToBoard(board: Board): Cell { 24 | return new Cell(this.position, this.kore, this.shipyardId, this.fleetId, board); 25 | } 26 | 27 | public get fleet(): Fleet | undefined { 28 | if (this.board.fleets.has(this.fleetId)) { 29 | return this.board.fleets.get(this.fleetId); 30 | } 31 | return undefined; 32 | } 33 | 34 | 35 | public get shipyard(): Shipyard | undefined { 36 | if (this.board.shipyards.has(this.shipyardId)) { 37 | return this.board.shipyards.get(this.shipyardId); 38 | } 39 | return undefined; 40 | } 41 | 42 | public neighbor(offset: Point): Cell { 43 | const next = this.position.translate(offset, this.board.size); 44 | return this.board.getCellAtPosition(next); 45 | } 46 | 47 | 48 | public north(): Cell { 49 | return this.neighbor(Direction.NORTH); 50 | } 51 | 52 | public south(): Cell { 53 | return this.neighbor((Direction.SOUTH)); 54 | } 55 | 56 | public east(): Cell { 57 | return this.neighbor(Direction.EAST); 58 | } 59 | 60 | public west(): Cell { 61 | return this.neighbor(Direction.WEST); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Configuration.ts: -------------------------------------------------------------------------------- 1 | export class Configuration { 2 | 3 | public readonly agentTimeout: number; 4 | public readonly startingKore: number; 5 | public readonly size: number; 6 | public readonly spawnCost: number; 7 | public readonly convertCost: number; 8 | public readonly regenRate: number; 9 | public readonly maxRegenCellKore: number; 10 | public readonly randomSeed: number; 11 | public readonly episodeSteps: number; 12 | 13 | public constructor(rawConfiguration: string) { 14 | const config = JSON.parse(rawConfiguration); 15 | this.agentTimeout = config.agentTimeout; 16 | this.startingKore = config.startingKore; 17 | this.size = config.size; 18 | this.spawnCost = config.spawnCost; 19 | this.convertCost = config.convertCost; 20 | this.regenRate = config.regenRate; 21 | this.maxRegenCellKore = config.maxRegenCellKore; 22 | this.randomSeed = config.randomSeed; 23 | this.episodeSteps = config.episodeSteps; 24 | } 25 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Fleet.ts: -------------------------------------------------------------------------------- 1 | import {Board} from "./Board"; 2 | import {Cell} from "./Cell"; 3 | import {Direction} from "./Direction"; 4 | import {Player} from "./Player"; 5 | import {Point} from "./Point"; 6 | 7 | export class Fleet { 8 | 9 | public readonly id: string; 10 | public shipCount: number; 11 | public direction: Direction; 12 | public position: Point; 13 | public flightPlan: string; 14 | public kore: number; 15 | public readonly playerId: number; 16 | public readonly board: Board; 17 | 18 | public constructor(fleetId: string, shipCount: number, direction: Direction, position: Point, kore: number, flightPlan: string, playerId: number, board: Board) { 19 | this.id = fleetId; 20 | this.shipCount = shipCount; 21 | this.direction = direction; 22 | this.position = position; 23 | this.flightPlan = flightPlan; 24 | this.kore = kore; 25 | this.playerId = playerId; 26 | this.board = board; 27 | } 28 | 29 | public cloneToBoard(board: Board): Fleet { 30 | return new Fleet(this.id, this.shipCount, this.direction, this.position, this.kore, this.flightPlan, this.playerId, board); 31 | } 32 | 33 | public get cell(): Cell { 34 | return this.board.getCellAtPosition(this.position); 35 | } 36 | 37 | public get player(): Player { 38 | return this.board.players[this.playerId]; 39 | } 40 | 41 | public get collectionRate(): number { 42 | return Math.min(Math.log(this.shipCount) / 10, .99); 43 | } 44 | 45 | /** 46 | * Returns the length of the longest possible flight plan this fleet can be assigned 47 | * @return 48 | */ 49 | public static maxFlightPlanLenForShipCount(shipCount: number): number { 50 | return (Math.floor(2 * Math.log(shipCount)) + 1); 51 | } 52 | 53 | /** 54 | * Converts a fleet back to the normalized observation subset that constructed it. 55 | */ 56 | public observation(): string[] { 57 | return [ 58 | this.position.toIndex(this.board.configuration.size).toString(), 59 | this.kore.toString(), 60 | this.shipCount.toString(), 61 | this.direction.toIndex().toString(), 62 | this.flightPlan 63 | ]; 64 | } 65 | 66 | public lessThanOtherAlliedFleet(other: Fleet): boolean { 67 | if (this.shipCount != other.shipCount) { 68 | return this.shipCount < other.shipCount; 69 | } 70 | if (this.kore != other.kore) { 71 | return this.kore < other.kore; 72 | } 73 | return this.direction.toIndex() > other.direction.toIndex(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/KoreIO.ts: -------------------------------------------------------------------------------- 1 | import readline from 'readline'; 2 | import { Board } from './Board'; 3 | import { ShipyardAction } from './ShipyardAction'; 4 | 5 | export class KoreIO { 6 | public getLine: () => Promise; 7 | 8 | public _setup(): void { 9 | // Prepare to read input 10 | const rl = readline.createInterface({ 11 | input: process.stdin, 12 | output: null, 13 | }); 14 | 15 | const buffer = []; 16 | let currentResolve: () => void; 17 | let currentPromise; 18 | const makePromise = function () { 19 | return new Promise((resolve) => { 20 | currentResolve = resolve; 21 | }); 22 | }; 23 | // on each line, push line to buffer 24 | rl.on('line', (line) => { 25 | buffer.push(line); 26 | currentResolve(); 27 | currentPromise = makePromise(); 28 | }); 29 | // The current promise for retrieving the next line 30 | currentPromise = makePromise(); 31 | 32 | // with await, we pause process until there is input 33 | this.getLine = async () => { 34 | return new Promise(async (resolve) => { 35 | while (buffer.length === 0) { 36 | // pause while buffer is empty, continue if new line read 37 | await currentPromise; 38 | } 39 | // once buffer is not empty, resolve the most recent line in stdin, and remove it 40 | resolve(buffer.shift()); 41 | }); 42 | }; 43 | } 44 | 45 | /** 46 | * Constructor for a new agent 47 | * User should edit this according to the `Design` this agent will compete under 48 | */ 49 | public constructor() { 50 | this._setup(); // DO NOT REMOVE 51 | } 52 | 53 | public async run(loop: (board: Board) => Promise): Promise { 54 | while (true) { 55 | const rawObservation = await this.getLine(); 56 | const rawConfiguration = await this.getLine(); 57 | const board = Board.fromRaw(rawObservation, rawConfiguration); 58 | try { 59 | const nextBoard = await loop(board); 60 | let actions = []; 61 | board.currentPlayer.nextActions.forEach((action: ShipyardAction, id: string) => 62 | actions.push(`${id}:${action.toString()}`) 63 | ); 64 | console.log(actions.join(',')); 65 | } catch (err) { 66 | console.log(err); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Observation.ts: -------------------------------------------------------------------------------- 1 | 2 | export class Observation { 3 | public readonly kore: number[]; 4 | public readonly playerHlt: number[]; 5 | public readonly playerShipyards: Map[]; 6 | public readonly playerFleets: Map[]; 7 | public readonly player: number; 8 | public readonly step: number; 9 | public readonly remainingOverageTime: number; 10 | 11 | public constructor(rawObservation: string) { 12 | const json = JSON.parse(rawObservation); 13 | this.kore = json["kore"]; 14 | this.player = json["player"]; 15 | this.step = json["step"]; 16 | this.remainingOverageTime = rawObservation["remainingOverageTime"]; 17 | const playerParts = json["players"]; 18 | this.playerHlt = []; 19 | this.playerShipyards = []; 20 | this.playerFleets = []; 21 | 22 | for (var i = 0; i < playerParts.length; i ++) { 23 | const playerPart = playerParts[i]; 24 | this.playerHlt.push(parseInt(playerPart[0])); 25 | 26 | const shipyards = new Map(); 27 | Object.entries(playerPart[1]).forEach(entry => { 28 | const shipyardId = entry[0]; 29 | const shipyardInts = entry[1]; 30 | shipyards.set(shipyardId, shipyardInts as number[]); 31 | }); 32 | this.playerShipyards.push(shipyards); 33 | 34 | const fleets = new Map(); 35 | Object.entries(playerPart[2]).forEach(entry => { 36 | const fleetId = entry[0]; 37 | const fleetStrs = entry[1]; 38 | fleets.set(fleetId, fleetStrs as string[]) 39 | }) 40 | this.playerFleets.push(fleets) 41 | } 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Pair.ts: -------------------------------------------------------------------------------- 1 | export class Pair { 2 | 3 | public readonly first: F; 4 | public readonly second: S; 5 | 6 | public constructor(first: F, second: S) { 7 | this.first = first; 8 | this.second = second; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Player.ts: -------------------------------------------------------------------------------- 1 | import {Board} from "./Board"; 2 | import {Fleet} from "./Fleet"; 3 | import {Shipyard} from "./Shipyard"; 4 | import {ShipyardAction} from "./ShipyardAction"; 5 | 6 | export class Player { 7 | public readonly id: number; 8 | public kore: number; 9 | public readonly shipyardIds: string[]; 10 | public readonly fleetIds: string[]; 11 | public readonly board: Board; 12 | 13 | public constructor(playerId: number, kore: number, shipyardIds: string[], fleetIds: string[], board: Board) { 14 | this.id = playerId; 15 | this.kore = kore; 16 | this.shipyardIds = shipyardIds; 17 | this.fleetIds = fleetIds; 18 | this.board = board; 19 | } 20 | 21 | public cloneToBoard(board: Board): Player { 22 | return new Player(this.id, this.kore, this.shipyardIds.slice(), this.fleetIds.slice(), board); 23 | } 24 | 25 | /** 26 | * Returns all shipyards owned by this player. 27 | * @return 28 | */ 29 | public get shipyards(): Shipyard[] { 30 | return Array.from(this.board.shipyards.values()) 31 | .filter(shipyard => this.shipyardIds.some(sId => sId == shipyard.id)); 32 | } 33 | 34 | /** 35 | * Returns all fleets owned by this player. 36 | */ 37 | public get fleets(): Fleet[] { 38 | return Array.from(this.board.fleets.values()) 39 | .filter(fleet => this.fleetIds.some(fId => fId == fleet.id)); 40 | } 41 | 42 | /** 43 | * Returns whether this player is the current player (generally if this returns True, this player is you. 44 | */ 45 | public isCurrentPlayer(): boolean { 46 | return this.id == this.board.currentPlayerId; 47 | } 48 | 49 | /** 50 | * Returns all queued fleet and shipyard actions for this player formatted for the kore interpreter to receive as an agent response. 51 | */ 52 | public get nextActions(): Map { 53 | const result = new Map(); 54 | this.shipyards.filter(shipyard => shipyard.nextAction).forEach(shipyard => result.set(shipyard.id, shipyard.nextAction as ShipyardAction)); 55 | return result; 56 | } 57 | 58 | /** 59 | * Converts a player back to the normalized observation subset that constructed it. 60 | */ 61 | public observation(): any[] { 62 | const shipyards = new Map(); 63 | this.shipyards.forEach(shipyard => shipyards.set(shipyard.id, shipyard.observation())); 64 | const fleets = new Map(); 65 | this.fleets.forEach(fleet => fleets.set(fleet.id, fleet.observation())); 66 | return [this.kore, shipyards, fleets]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Point.ts: -------------------------------------------------------------------------------- 1 | export class Point { 2 | public readonly x: number; 3 | public readonly y: number; 4 | 5 | public constructor(x: number, y: number) { 6 | this.x = x; 7 | this.y = y; 8 | } 9 | 10 | public translate(offset: Point, size: number): Point { 11 | return this.add(offset).mod(size); 12 | } 13 | 14 | public add(other: Point): Point { 15 | return new Point(this.x + other.x, this.y + other.y); 16 | } 17 | 18 | public mod(size: number): Point { 19 | // handle cases where the point is negative due to translation offset being negative 20 | return new Point((this.x + size) % size, (this.y + size) % size); 21 | } 22 | 23 | /** 24 | * Gets the manhatten distance between two points 25 | */ 26 | public distanceTo(other: Point, size: number): number { 27 | const abs_x = Math.abs(this.x - other.x); 28 | const dist_x = abs_x < size/2 ? abs_x : size - abs_x; 29 | const abs_y = Math.abs(this.y - other.y); 30 | const dist_y = abs_y < size/2 ? abs_y : size - abs_y; 31 | return dist_x + dist_y; 32 | } 33 | 34 | /** 35 | * Converts a 2d position in the form (x, y) to an index in the observation.kore list. 36 | * See fromIndex for the inverse. 37 | */ 38 | public toIndex(size: number) { 39 | return (size - this.y - 1) * size + this.x; 40 | } 41 | 42 | public static fromIndex(index: number, size: number): Point { 43 | return new Point(index % size, size - Math.floor(index/size) - 1); 44 | } 45 | 46 | public abs(): Point { 47 | return new Point(Math.abs(this.x), Math.abs(this.y)); 48 | } 49 | 50 | public equals(other: Point): boolean { 51 | return this.x == other.x && this.y == other.y; 52 | } 53 | 54 | public toString(): string { 55 | return "(" + this.x + "," + this.y + ")"; 56 | } 57 | 58 | public multiply(factor: number): Point { 59 | return new Point(factor * this.x, factor * this.y); 60 | } 61 | 62 | public subtract(other: Point): Point { 63 | return new Point(this.x - other.x, this.y - other.y); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Shipyard.ts: -------------------------------------------------------------------------------- 1 | 2 | import {Board} from "./Board"; 3 | import {Cell} from "./Cell"; 4 | import {Player} from "./Player"; 5 | import {Point} from "./Point"; 6 | import {ShipyardAction} from "./ShipyardAction"; 7 | 8 | const SPAWN_VALUES = []; 9 | const upgradeTimes: number[] = []; 10 | for(let i = 1; i < 10; i++) { 11 | upgradeTimes[i-1] = Math.pow(i, 2) + 1; 12 | } 13 | let current = 0; 14 | for(let i = 1; i < 10; i++) { 15 | current += upgradeTimes[i-1]; 16 | SPAWN_VALUES[i-1] = current; 17 | } 18 | 19 | export class Shipyard { 20 | public readonly id: string; 21 | public shipCount: number; 22 | public position: Point; 23 | public playerId: number; 24 | public turnsControlled: number; 25 | public readonly board: Board; 26 | public nextAction: ShipyardAction | undefined; 27 | 28 | public constructor(shipyardId: string, shipCount: number, position: Point, playerId: number, turnsControlled: number, board: Board, nextAction: ShipyardAction | undefined) { 29 | this.id = shipyardId; 30 | this.shipCount = shipCount; 31 | this.position = position; 32 | this.playerId = playerId; 33 | this.turnsControlled = turnsControlled; 34 | this.board = board; 35 | this.nextAction = nextAction; 36 | } 37 | 38 | public cloneToBoard(board: Board): Shipyard { 39 | return new Shipyard(this.id, this.shipCount, this.position, this.playerId, this.turnsControlled, board, this.nextAction); 40 | } 41 | 42 | public setNextAction(action: ShipyardAction): void { 43 | this.nextAction = action; 44 | } 45 | 46 | public get maxSpawn(): number { 47 | for (let i = 0; i < SPAWN_VALUES.length; i++) { 48 | if (this.turnsControlled < SPAWN_VALUES[i]) { 49 | return i + 1; 50 | } 51 | } 52 | return SPAWN_VALUES.length + 1; 53 | } 54 | 55 | /** 56 | * Returns the cell this shipyard is on. 57 | */ 58 | public get cell(): Cell { 59 | return this.board.getCellAtPosition(this.position); 60 | } 61 | 62 | public get player(): Player { 63 | return this.board.players[this.playerId]; 64 | } 65 | 66 | /** 67 | * Converts a shipyard back to the normalized observation subset that constructed it. 68 | */ 69 | public observation(): number[] { 70 | return [this.position.toIndex(this.board.configuration.size), this.shipCount, this.turnsControlled]; 71 | } 72 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/ShipyardAction.ts: -------------------------------------------------------------------------------- 1 | 2 | export class ShipyardAction { 3 | public static readonly SPAWN = "SPAWN"; 4 | public static readonly LAUNCH = "LAUNCH"; 5 | public readonly actionType: string; 6 | public readonly shipCount: number; 7 | public readonly flightPlan: string; 8 | 9 | public static spawnShips(shipCount: number): ShipyardAction { 10 | return new ShipyardAction(ShipyardAction.SPAWN, shipCount, ""); 11 | } 12 | 13 | public static launchFleetWithFlightPlan(shipCount: number, flightPlan: string): ShipyardAction { 14 | return new ShipyardAction(ShipyardAction.LAUNCH, shipCount, flightPlan); 15 | } 16 | 17 | public static fromstring(raw: string): ShipyardAction { 18 | if (raw.length == 0) { 19 | throw new Error("invalid raw shipyard empty string"); 20 | } 21 | const shipCount = parseInt(raw.split("_")[1]); 22 | if (raw.startsWith(ShipyardAction.LAUNCH)) { 23 | return ShipyardAction.spawnShips(shipCount); 24 | } 25 | if (raw.startsWith(ShipyardAction.SPAWN)) { 26 | const flightPlan = raw.split("_")[2]; 27 | return ShipyardAction.launchFleetWithFlightPlan(shipCount, flightPlan); 28 | } 29 | throw new Error("invalid Shipyard Action raw " + raw); 30 | } 31 | 32 | public constructor(type: string, shipCount: number, flightPlan: string) { 33 | // assert type.equals(SPAWN) || type.equals(LAUNCH) : "Type must be SPAWN or LAUNCH"; 34 | // assert shipCount > 0 : "numShips must be a non-negative number"; 35 | this.actionType = type; 36 | this.shipCount = shipCount; 37 | this.flightPlan = flightPlan; 38 | } 39 | 40 | private get isSpawn(): boolean { 41 | return this.actionType == ShipyardAction.SPAWN; 42 | } 43 | 44 | private get isLaunch(): boolean { 45 | return this.actionType == ShipyardAction.LAUNCH; 46 | } 47 | 48 | public toString(): string { 49 | if (this.isSpawn) { 50 | return `${ShipyardAction.SPAWN}_${this.shipCount}`; 51 | } 52 | if (this.isLaunch) { 53 | return `${ShipyardAction.LAUNCH}_${this.shipCount}_${this.flightPlan}`; 54 | } 55 | throw new Error("invalid Shpyard Action"); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/main.py: -------------------------------------------------------------------------------- 1 | from subprocess import Popen, PIPE 2 | from threading import Thread 3 | from queue import Queue, Empty 4 | 5 | import atexit 6 | import os 7 | import sys 8 | agent_processes = [None, None, None, None] 9 | t = None 10 | q = None 11 | def cleanup_process(): 12 | global agent_processes 13 | for proc in agent_processes: 14 | if proc is not None: 15 | proc.kill() 16 | def enqueue_output(out, queue): 17 | for line in iter(out.readline, b''): 18 | queue.put(line) 19 | out.close() 20 | 21 | def agent(observation, configuration): 22 | global agent_processes, t, q 23 | 24 | agent_process = agent_processes[observation.player] 25 | ### Do not edit ### 26 | if agent_process is None: 27 | if "__raw_path__" in configuration: 28 | cwd = os.path.dirname(configuration["__raw_path__"]) 29 | else: 30 | cwd = os.path.dirname(__file__) 31 | agent_process = Popen(["node", "dist/Bot.js"], stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=cwd) 32 | agent_processes[observation.player] = agent_process 33 | atexit.register(cleanup_process) 34 | 35 | # following 4 lines from https://stackoverflow.com/questions/375427/a-non-blocking-read-on-a-subprocess-pipe-in-python 36 | q = Queue() 37 | t = Thread(target=enqueue_output, args=(agent_process.stderr, q)) 38 | t.daemon = True # thread dies with the program 39 | t.start() 40 | 41 | # print observations to agent 42 | import json 43 | agent_process.stdin.write((json.dumps(observation) + "\n").encode()) 44 | agent_process.stdin.write((json.dumps(configuration) + "\n").encode()) 45 | agent_process.stdin.flush() 46 | 47 | # wait for data written to stdout 48 | agent1res = (agent_process.stdout.readline()).decode() 49 | 50 | while True: 51 | try: line = q.get_nowait() 52 | except Empty: 53 | # no standard error received, break 54 | break 55 | else: 56 | # standard error output received, print it out 57 | print(line.decode(), file=sys.stderr, end='') 58 | 59 | agent1res = agent1res.strip() 60 | outputs = agent1res.split(",") 61 | actions = {} 62 | for cmd in outputs: 63 | if cmd != "": 64 | shipyard_id, action_str = cmd.split(":") 65 | actions[shipyard_id] = action_str 66 | return actions 67 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/miner.py: -------------------------------------------------------------------------------- 1 | from subprocess import Popen, PIPE 2 | from threading import Thread 3 | from queue import Queue, Empty 4 | 5 | import atexit 6 | import os 7 | import sys 8 | agent_processes = [None, None, None, None] 9 | t = None 10 | q = None 11 | def cleanup_process(): 12 | global agent_processes 13 | for proc in agent_processes: 14 | if proc is not None: 15 | proc.kill() 16 | def enqueue_output(out, queue): 17 | for line in iter(out.readline, b''): 18 | queue.put(line) 19 | out.close() 20 | 21 | def agent(observation, configuration): 22 | global agent_processes, t, q 23 | 24 | agent_process = agent_processes[observation.player] 25 | ### Do not edit ### 26 | if agent_process is None: 27 | if "__raw_path__" in configuration: 28 | cwd = os.path.dirname(configuration["__raw_path__"]) 29 | else: 30 | cwd = os.path.dirname(__file__) 31 | agent_process = Popen(["node", "dist/MinerBot.js"], stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=cwd) 32 | agent_processes[observation.player] = agent_process 33 | atexit.register(cleanup_process) 34 | 35 | # following 4 lines from https://stackoverflow.com/questions/375427/a-non-blocking-read-on-a-subprocess-pipe-in-python 36 | q = Queue() 37 | t = Thread(target=enqueue_output, args=(agent_process.stderr, q)) 38 | t.daemon = True # thread dies with the program 39 | t.start() 40 | 41 | # print observations to agent 42 | import json 43 | agent_process.stdin.write((json.dumps(observation) + "\n").encode()) 44 | agent_process.stdin.write((json.dumps(configuration) + "\n").encode()) 45 | agent_process.stdin.flush() 46 | 47 | # wait for data written to stdout 48 | agent1res = (agent_process.stdout.readline()).decode() 49 | 50 | while True: 51 | try: line = q.get_nowait() 52 | except Empty: 53 | # no standard error received, break 54 | break 55 | else: 56 | # standard error output received, print it out 57 | print(line.decode(), file=sys.stderr, end='') 58 | 59 | agent1res = agent1res.strip() 60 | outputs = agent1res.split(",") 61 | actions = {} 62 | for cmd in outputs: 63 | if cmd != "": 64 | shipyard_id, action_str = cmd.split(":") 65 | actions[shipyard_id] = action_str 66 | return actions 67 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@types/chai": "^4.3.0", 4 | "@types/mocha": "^9.1.0", 5 | "chai": "^4.3.4", 6 | "mocha": "^9.2.0", 7 | "ts-node": "^10.4.0", 8 | "typescript": "^4.5.5" 9 | }, 10 | "scripts": { 11 | "test": "mocha --require ts-node/register test/**/*.ts", 12 | "compile": "tsc", 13 | "package": "tsc && tar -czvf submission.tar.gz main.py dist/*", 14 | "interpreter:run": "node --require ts-node/register interpreter.ts run 2 ./main.py miner", 15 | "interpreter:step": "node --require ts-node/register interpreter.ts step 2 miner do_nothing", 16 | "watch4": "kaggle-environments run --environment kore_fleets --agents ./main.py ./main.py ./main.py ./main.py --log out.log --render '{\"mode\": \"html\"}' --out replay.html && google-chrome replay.html", 17 | "watch2": "kaggle-environments run --environment kore_fleets --agents ./main.py ./main.py --log out.log --render '{\"mode\": \"html\"}' --out replay.html && google-chrome replay.html", 18 | "watch1": "kaggle-environments run --environment kore_fleets --agents ./main.py --log out.log --render '{\"mode\": \"html\"}' --out replay.html && google-chrome replay.html", 19 | "play4": "kaggle-environments run --environment kore_fleets --agents ./main.py ./main.py ./main.py ./main.py --log out.log --out replay.html", 20 | "play2": "kaggle-environments run --environment kore_fleets --agents ./main.py ./main.py --log out.log --out replay.html", 21 | "play1": "kaggle-environments run --environment kore_fleets --agents ./main.py --log out.log --out replay.html" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/test/ConfigurationTest.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'mocha'; 2 | import { expect } from 'chai'; 3 | 4 | import * as fs from 'fs'; 5 | 6 | import { Configuration } from "../kore/Configuration"; 7 | 8 | describe('Configuration', () => { 9 | it('init works correctly', () => { 10 | const rawConfig = fs.readFileSync('./test/configuration.json','utf8'); 11 | 12 | const config = new Configuration(rawConfig); 13 | 14 | expect(config.regenRate).to.equal(.02); 15 | }) 16 | }); -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/test/ObservationTest.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'mocha'; 2 | import { expect } from 'chai'; 3 | 4 | import * as fs from 'fs'; 5 | 6 | import { Observation } from "../kore/Observation"; 7 | 8 | describe('Observation', () => { 9 | it('valid observation works', () => { 10 | const rawObs = fs.readFileSync('./test/observation.json', 'utf8'); 11 | const ob = new Observation(rawObs); 12 | 13 | expect(ob.player).to.equal(0); 14 | expect(ob.step).to.equal(16); 15 | expect(ob.playerFleets.length).to.equal(4); 16 | expect(ob.playerFleets[0].size).to.equal(0); 17 | expect(ob.playerShipyards.length).to.equal(4); 18 | expect(ob.playerShipyards[0].size).to.equal(1); 19 | }) 20 | 21 | it('full observation works', () => { 22 | const rawObs = fs.readFileSync('./test/fullob.json', 'utf8'); 23 | const ob = new Observation(rawObs); 24 | 25 | expect(ob.player).to.equal(0); 26 | expect(ob.step).to.equal(200); 27 | expect(ob.playerHlt.length).to.equal(2); 28 | expect(ob.playerFleets.length).to.equal(2); 29 | expect(ob.playerFleets[0].size).to.equal(1); 30 | expect(ob.playerShipyards.length).to.equal(2); 31 | expect(ob.playerShipyards[0].size).to.equal(6); 32 | }) 33 | }); -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/test/PointTest.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'mocha'; 2 | import { expect } from 'chai'; 3 | 4 | import { Point } from "../kore/Point"; 5 | 6 | describe('Point', () => { 7 | it('fontIndex toIndex isIdetity', () => { 8 | const idx = 254; 9 | const size = 31; 10 | 11 | const point = Point.fromIndex(idx, size); 12 | const mirroredIdx = point.toIndex(size); 13 | 14 | expect(mirroredIdx).to.equal(idx); 15 | }) 16 | 17 | }); 18 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/test/ShipyardTest.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'mocha'; 2 | import { expect } from 'chai'; 3 | 4 | import { Shipyard } from "../kore/Shipyard"; 5 | import { Point } from '../kore/Point'; 6 | 7 | describe('Shipyard', () => { 8 | const turns = [0, 1, 2, 293, 294, 295]; 9 | const expected = [1, 1, 2, 9, 10, 10] 10 | for (let i = 0; i < turns.length; i ++) { 11 | it(`max spawn is correct at ${turns[i]} turns controlled`, () => { 12 | const shipyard = new Shipyard("A", 0, new Point(0, 0), 1, turns[i], null, null); 13 | 14 | expect(shipyard.maxSpawn).to.equal(expected[i]); 15 | }) 16 | } 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/test/configuration.json: -------------------------------------------------------------------------------- 1 | {"episodeSteps": 401, "actTimeout": 3, "runTimeout": 9600, "startingKore": 6000, "size": 31, "spawnCost": 10, "convertCost": 50, "regenRate": 0.02, "maxRegenCellKore": 500, "agentTimeout": 60, "randomSeed": 1464814655, "__raw_path__": "java/main.py"} 2 | -------------------------------------------------------------------------------- /kaggle_environments/envs/kore_fleets/starter_bots/ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "lib": ["esnext"], 5 | "target": "es5", 6 | "moduleResolution": "node", 7 | "experimentalDecorators": true, 8 | "emitDecoratorMetadata": true, 9 | "esModuleInterop": true, 10 | "sourceMap": true, 11 | "declaration": true, 12 | "resolveJsonModule": true, 13 | "outDir": "dist" 14 | }, 15 | "include": [ 16 | "**/*.ts" 17 | ], 18 | "exclude": [ 19 | "node_modules", 20 | "dist" 21 | ] 22 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/llm_20_questions/llm_20_questions.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "llm_20_questions", 3 | "title": "20 Questions", 4 | "description": "20 Questions played between two LLM agents", 5 | "version": "1.0.0", 6 | "agents": [4], 7 | "configuration": { 8 | "episodeSteps": 61, 9 | "actTimeout": 60, 10 | "runTimeout": 9600, 11 | "agentTimeout": { 12 | "description": "Obsolete field kept for backwards compatibility, please use observation.remainingOverageTime.", 13 | "type": "number", 14 | "minimum": 0, 15 | "default": 3600 16 | } 17 | }, 18 | "reward": { 19 | "description": "1-20 = Won, -1 = Lost", 20 | "enum": [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], 21 | "defaults": [0, 0, 0, 0] 22 | }, 23 | "observation": { 24 | "questions": { 25 | "description": "Questions the guessing agent has asked.", 26 | "type": "array", 27 | "default": [] 28 | }, 29 | "guesses": { 30 | "description": "Guesses the guessing agent has made.", 31 | "type": "array", 32 | "default": [] 33 | }, 34 | "answers": { 35 | "description": "Answers the answering agent has given.", 36 | "type": "array", 37 | "default": [] 38 | }, 39 | "role": { 40 | "description": "The role of the agent for this episode.", 41 | "enum": ["guesser", "answerer"], 42 | "defaults": ["guesser", "answerer", "guesser", "answerer"] 43 | }, 44 | "turnType": { 45 | "description": "The type of action that should be taken this turn.", 46 | "enum": ["ask", "guess", "answer"], 47 | "defaults": ["ask", "answer", "ask", "answer"] 48 | }, 49 | "keyword": { 50 | "description": "The keyword to win the game", 51 | "type": "string", 52 | "shared": "false", 53 | "default": "" 54 | }, 55 | "category": { 56 | "description": "The catagory of the keyword.", 57 | "type": "string", 58 | "shared": "false", 59 | "default": "" 60 | }, 61 | "remainingOverageTime": 300 62 | }, 63 | "action": { 64 | "description": "LLM agent response.", 65 | "type": "string", 66 | "default": "" 67 | }, 68 | "status": { 69 | "defaults": ["ACTIVE", "INACTIVE", "ACTIVE", "INACTIVE"] 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /kaggle_environments/envs/llm_20_questions/test_llm_20_questions.py: -------------------------------------------------------------------------------- 1 | 2 | from kaggle_environments import make 3 | 4 | def custom_questioner(obs): 5 | if obs.turnType == "guess": 6 | return "banana" 7 | return "Is it a banana?" 8 | 9 | def last_round_guesser_error(obs): 10 | if obs.turnType == "guess" and len(obs.questions) == 20: 11 | a = 1 12 | b = 0 13 | return a / b 14 | if obs.turnType == "guess": 15 | return "banana" 16 | return "Is it a banana?" 17 | 18 | def custom_answerer(): 19 | return "no" 20 | 21 | def bad_answerer(): 22 | return "maybe?" 23 | 24 | def error_agent(): 25 | raise ValueError 26 | 27 | def test_llm_20_q_completes(): 28 | env = make("llm_20_questions", debug=True) 29 | env.run([custom_questioner, custom_answerer, custom_questioner, custom_answerer]) 30 | json = env.toJSON() 31 | assert json["name"] == "llm_20_questions" 32 | assert json["statuses"] == ["DONE", "DONE", "DONE", "DONE"] 33 | 34 | def test_llm_20_q_errors_on_bad_answer(): 35 | env = make("llm_20_questions", debug=True) 36 | env.run([custom_questioner, custom_answerer, custom_questioner, bad_answerer]) 37 | json = env.toJSON() 38 | assert json["name"] == "llm_20_questions" 39 | assert json["rewards"] == [1, 1, 1, None] 40 | assert json["statuses"] == ["DONE", "DONE", "DONE", "ERROR"] 41 | print(len(json["steps"])) 42 | assert len(json["steps"]) == 3 43 | 44 | def test_llm_20_q_errors_on_error_answer(): 45 | env = make("llm_20_questions", debug=True) 46 | env.run([custom_questioner, custom_answerer, custom_questioner, error_agent]) 47 | json = env.toJSON() 48 | assert json["name"] == "llm_20_questions" 49 | assert json["rewards"] == [1, 1, 1, None] 50 | assert json["statuses"] == ["DONE", "DONE", "DONE", "ERROR"] 51 | assert len(json["steps"]) == 3 52 | 53 | def test_llm_20_q_errors_on_error_question(): 54 | env = make("llm_20_questions", debug=True) 55 | env.run([custom_questioner, custom_answerer, error_agent, custom_answerer]) 56 | json = env.toJSON() 57 | assert json["name"] == "llm_20_questions" 58 | assert json["rewards"] == [1, 1, None, 1] 59 | assert json["statuses"] == ["DONE", "DONE", "ERROR", "DONE"] 60 | assert len(json["steps"]) == 2 61 | 62 | def test_llm_20_q_errors_on_error_last_guess(): 63 | env = make("llm_20_questions", debug=True) 64 | env.run([custom_questioner, custom_answerer, last_round_guesser_error, custom_answerer]) 65 | json = env.toJSON() 66 | assert json["name"] == "llm_20_questions" 67 | assert json["rewards"] == [1, 1, None, 1] 68 | assert json["statuses"] == ["DONE", "DONE", "ERROR", "DONE"] -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/README.md: -------------------------------------------------------------------------------- 1 | # Lux AI Challenge Season 1 2 | 3 | Welcome to the Kaggle Environments wrapped around the Lux AI Challenge Season 1! For more information or if you have an issues or want to make contributes, check out the main repository at https://github.com/Lux-AI-Challenge/Lux-Design-2021 4 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_2021/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/agents.py: -------------------------------------------------------------------------------- 1 | from kaggle_environments.envs.lux_ai_2021.test_agents.python.random_agent import random_agent 2 | import random 3 | from .test_agents.js_simple.main import js_agent as js_simple_agent 4 | from .test_agents.python.random_agent import random_agent 5 | from .test_agents.python.simple_agent import agent as simple_agent 6 | agents = { 7 | "random_agent": random_agent, 8 | "simple_agent": simple_agent, 9 | "js_simple_agent": js_simple_agent, 10 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | Lux AI Challenge Viewer 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 30 | 31 | 32 |
33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/lux_ai_2021.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lux_ai_2021", 3 | "title": "Lux AI Challenge: 2021", 4 | "description": "A Novel AI Programming Challenge about Lux", 5 | "version": "3.1.0", 6 | "agents": [2], 7 | "configuration": { 8 | "episodeSteps": { 9 | "description": "Maximum number of steps the environment can run. Total is this number -1. One complete game is 360 steps", 10 | "type": "integer", 11 | "minimum": 2, 12 | "default": 361 13 | }, 14 | "parameters": { 15 | "description": "Parameters for Lux AI 2021", 16 | "type": "object" 17 | }, 18 | "mapType": { 19 | "description": "Map type to use", 20 | "type": "string", 21 | "default": "random" 22 | }, 23 | "width": { 24 | "description": "Width of map to generate", 25 | "type": "integer" 26 | }, 27 | "seed": { 28 | "description": "Seed to use for episodes", 29 | "type": "integer" 30 | }, 31 | "annotations": { 32 | "description": "Whether to enable debug annotations or not.", 33 | "type": "boolean", 34 | "default": false 35 | }, 36 | "actTimeout": 3, 37 | "loglevel": { 38 | "description": "Logging level of the game. 2 for warnings (e.g unit collisions, malformed actions), 1 for errors, 0 for none", 39 | "type": "integer", 40 | "default": 0 41 | } 42 | }, 43 | "reward": { 44 | "description": "Reward of the agent. Equal to number of city tiles * 1000 + number of units", 45 | "type": "integer", 46 | "default": 0 47 | }, 48 | "observation": { 49 | "remainingOverageTime": 60, 50 | "width": { 51 | "description": "The width of the map", 52 | "type": "integer", 53 | "shared": true, 54 | "default": 12 55 | }, 56 | "height": { 57 | "description": "The height of the map", 58 | "type": "integer", 59 | "shared": true, 60 | "default": 12 61 | }, 62 | "reward": { 63 | "description": "Current reward of the agent. Equal to number of city tiles * 10000 + number of units", 64 | "type": "integer", 65 | "default": 0 66 | }, 67 | "updates": { 68 | "description": "List of update strings for agents to use and generate the observed state", 69 | "type": "array", 70 | "shared": true, 71 | "items": { 72 | "type": "string" 73 | } 74 | }, 75 | "globalUnitIDCount": { 76 | "description": "Used purely to ensure state is the exact same including generated IDs", 77 | "type": "integer", 78 | "shared": true, 79 | "default": 0 80 | }, 81 | "globalCityIDCount": { 82 | "description": "Used purely to ensure state is the exact same including generated IDs", 83 | "type": "integer", 84 | "shared": true, 85 | "default": 0 86 | }, 87 | "player": { 88 | "description": "Current player's index / team id", 89 | "type": "integer", 90 | "defaults": [0, 1] 91 | } 92 | }, 93 | "action": { 94 | "description": "Actions", 95 | "type": "array", 96 | "items": { 97 | "type": "string" 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_2021/test_agents/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/game_constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * All game constants 3 | */ 4 | const GAME_CONSTANTS = require('./game_constants.json'); 5 | 6 | module.exports = GAME_CONSTANTS; -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/game_constants.json: -------------------------------------------------------------------------------- 1 | { 2 | "UNIT_TYPES": { 3 | "WORKER": 0, 4 | "CART": 1 5 | }, 6 | "RESOURCE_TYPES": { 7 | "WOOD": "wood", 8 | "COAL": "coal", 9 | "URANIUM": "uranium" 10 | }, 11 | "DIRECTIONS": { 12 | "NORTH": "n", 13 | "WEST": "w", 14 | "EAST": "e", 15 | "SOUTH": "s", 16 | "CENTER": "c" 17 | }, 18 | "PARAMETERS": { 19 | "DAY_LENGTH": 30, 20 | "NIGHT_LENGTH": 10, 21 | "MAX_DAYS": 360, 22 | "LIGHT_UPKEEP": { 23 | "CITY": 30, 24 | "WORKER": 4, 25 | "CART": 10 26 | }, 27 | "WOOD_GROWTH_RATE": 1.01, 28 | "MAX_WOOD_AMOUNT": 500, 29 | "CITY_BUILD_COST": 100, 30 | "CITY_ADJACENCY_BONUS": 5, 31 | "RESOURCE_CAPACITY": { 32 | "WORKER": 100, 33 | "CART": 2000 34 | }, 35 | "WORKER_COLLECTION_RATE": { 36 | "WOOD": 20, 37 | "COAL": 5, 38 | "URANIUM": 2 39 | }, 40 | "RESOURCE_TO_FUEL_RATE": { 41 | "WOOD": 1, 42 | "COAL": 10, 43 | "URANIUM": 40 44 | }, 45 | "RESEARCH_REQUIREMENTS": { 46 | "COAL": 50, 47 | "URANIUM": 200 48 | }, 49 | "CITY_ACTION_COOLDOWN": 10, 50 | "UNIT_ACTION_COOLDOWN": { 51 | "CART": 3, 52 | "WORKER": 2 53 | }, 54 | "MAX_ROAD": 6, 55 | "MIN_ROAD": 0, 56 | "CART_ROAD_DEVELOPMENT_RATE": 0.5, 57 | "PILLAGE_RATE": 0.5 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/io.js: -------------------------------------------------------------------------------- 1 | /** all constants related to any input from match engine */ 2 | const INPUT_CONSTANTS = { 3 | DONE: 'D_DONE', 4 | RESEARCH_POINTS: 'rp', 5 | RESOURCES: 'r', 6 | UNITS: 'u', 7 | CITY: 'c', 8 | CITY_TILES: 'ct', 9 | ROADS: 'ccd', 10 | } 11 | 12 | module.exports = { 13 | INPUT_CONSTANTS, 14 | } 15 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/map.js: -------------------------------------------------------------------------------- 1 | const GAME_CONSTANTS = require('./game_constants'); 2 | const DIRECTIONS = GAME_CONSTANTS.DIRECTIONS; 3 | class GameMap { 4 | constructor(width, height) { 5 | this.height = height; 6 | this.width = width; 7 | this.map = new Array(this.height); 8 | 9 | for (let y = 0; y < this.height; y++) { 10 | this.map[y] = new Array(this.width); 11 | for (let x = 0; x < this.width; x++) { 12 | this.map[y][x] = new Cell(x, y); 13 | } 14 | } 15 | } 16 | getCellByPos(pos) { 17 | return this.map[pos.y][pos.x]; 18 | } 19 | getCell(x, y) { 20 | return this.map[y][x]; 21 | } 22 | _setResource(type, x, y, amount) { 23 | const cell = this.getCell(x, y); 24 | cell.resource = { 25 | type: type, 26 | amount: amount 27 | } 28 | } 29 | 30 | } 31 | 32 | class Cell { 33 | constructor(x, y) { 34 | this.pos = new Position(x, y); 35 | this.resource = null; 36 | this.citytile = null; 37 | this.road = 0; 38 | } 39 | hasResource() { 40 | return this.resource !== null && this.resource.amount > 0; 41 | } 42 | } 43 | 44 | class Position { 45 | constructor(x, y) { 46 | this.x = x; 47 | this.y = y; 48 | } 49 | isAdjacent(pos) { 50 | const dx = this.x - pos.x; 51 | const dy = this.y - pos.y; 52 | if (Math.abs(dx) + Math.abs(dy) > 1) { 53 | return false; 54 | } 55 | return true; 56 | } 57 | equals(pos) { 58 | return this.x === pos.x && this.y === pos.y; 59 | } 60 | 61 | translate(direction, units) { 62 | switch (direction) { 63 | case DIRECTIONS.NORTH: 64 | return new Position(this.x, this.y - units); 65 | case DIRECTIONS.EAST: 66 | return new Position(this.x + units, this.y); 67 | case DIRECTIONS.SOUTH: 68 | return new Position(this.x, this.y + units); 69 | case DIRECTIONS.WEST: 70 | return new Position(this.x - units, this.y); 71 | case DIRECTIONS.CENTER: 72 | return new Position(this.x, this.y); 73 | } 74 | } 75 | 76 | /** Returns Manhattan distance to pos from this position */ 77 | distanceTo(pos) { 78 | return Math.abs(pos.x - this.x) + Math.abs(pos.y - this.y); 79 | } 80 | 81 | /** Returns closest direction to targetPos, or null if staying put is best */ 82 | directionTo(targetPos) { 83 | const checkDirections = [ 84 | DIRECTIONS.NORTH, 85 | DIRECTIONS.EAST, 86 | DIRECTIONS.SOUTH, 87 | DIRECTIONS.WEST, 88 | ]; 89 | let closestDirection = DIRECTIONS.CENTER; 90 | let closestDist = this.distanceTo(targetPos); 91 | checkDirections.forEach((dir) => { 92 | const newpos = this.translate(dir, 1); 93 | const dist = targetPos.distanceTo(newpos); 94 | if (dist < closestDist) { 95 | closestDist = dist; 96 | closestDirection = dir; 97 | } 98 | }); 99 | return closestDirection; 100 | } 101 | } 102 | 103 | module.exports = { 104 | GameMap, 105 | Cell, 106 | Position, 107 | } 108 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/parser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Parser class to help parse a input line of data 3 | */ 4 | class Parser { 5 | 6 | constructor(d = ',') { 7 | this.delimiter = d; 8 | return this.parse.bind(this); 9 | } 10 | setDelimeter(s) { 11 | this.delimiter = s; 12 | } 13 | parse(str) { 14 | return new Parsed(str, this.delimiter); 15 | } 16 | 17 | } 18 | class Parsed { 19 | constructor(str, d) { 20 | this.str = str; 21 | this.contents = str.split(d); 22 | 23 | // remove the last element if its empty string 24 | if (this.contents[this.contents.length - 1] === '') { 25 | this.contents = this.contents.slice(0, this.contents.length - 1); 26 | } 27 | this.index = 0; 28 | } 29 | _nextStr() { 30 | if (this.index < this.contents.length) { 31 | return this.contents[this.index++]; 32 | } 33 | else { 34 | throw new Error("No more contents to consume from line") 35 | } 36 | } 37 | // Returns the remainder of the line as an array of integers 38 | nextIntArr() { 39 | if (this.index < this.contents.length) { 40 | let remainder = this.contents.slice(this.index, this.contents.length).map((val) => parseInt(val)); 41 | return remainder; 42 | } 43 | else { 44 | throw new Error("No more contents to consume from line") 45 | } 46 | } 47 | nextInt() { 48 | let str = this._nextStr(); 49 | return parseInt(str); 50 | } 51 | // Returns the remainder of the line as an array of floats 52 | nextFloatArr() { 53 | if (this.index < this.contents.length) { 54 | let remainder = this.contents.slice(this.index++).map((val) => parseFloat(val)); 55 | return remainder; 56 | } 57 | else { 58 | throw new Error("No more contents to consume from line") 59 | } 60 | } 61 | nextFloat() { 62 | let str = this._nextStr(); 63 | return parseFloat(str); 64 | } 65 | // Returns the remainder of the line as an array of strings 66 | nextStrArr() { 67 | if (this.index < this.contents.length) { 68 | let remainder = this.contents.slice(this.index++); 69 | return remainder; 70 | } 71 | else { 72 | throw new Error("No more contents to consume from line") 73 | } 74 | } 75 | nextStr() { 76 | return this._nextStr(); 77 | } 78 | } 79 | module.exports = Parser -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/main.js: -------------------------------------------------------------------------------- 1 | const kit = require('./lux/kit'); 2 | const GAME_CONSTANTS = require('./lux/game_constants'); 3 | const DIRECTIONS = GAME_CONSTANTS.DIRECTIONS; 4 | // create a new agent 5 | const agent = new kit.Agent(); 6 | const annotate = kit.annotate; 7 | 8 | // first initialize the agent, and then proceed to go in a loop waiting for updates and running the AI 9 | agent.initialize().then(async () => { 10 | while (true) { 11 | /** Do not edit! **/ 12 | // wait for updates 13 | await agent.update(); 14 | 15 | const actions = []; 16 | const gameState = agent.gameState; 17 | /** AI Code Goes Below! **/ 18 | 19 | const player = gameState.players[gameState.id]; 20 | const opponent = gameState.players[(gameState.id + 1) % 2]; 21 | const gameMap = gameState.map; 22 | 23 | const resourceTiles = []; 24 | for (let y = 0; y < gameMap.height; y++) { 25 | for (let x = 0; x < gameMap.width; x++) { 26 | const cell = gameMap.getCell(x, y); 27 | if (cell.hasResource()) { 28 | resourceTiles.push(cell); 29 | } 30 | } 31 | } 32 | 33 | // we iterate over all our units and do something with them 34 | for (let i = 0; i < player.units.length; i++) { 35 | const unit = player.units[i]; 36 | if (unit.isWorker() && unit.canAct()) { 37 | if (unit.getCargoSpaceLeft() > 0) { 38 | // if the unit is a worker and we have space in cargo, lets find the nearest resource tile and try to mine it 39 | let closestResourceTile = null; 40 | let closestDist = 9999999; 41 | resourceTiles.forEach((cell) => { 42 | if (cell.resource.type === GAME_CONSTANTS.RESOURCE_TYPES.COAL && !player.researchedCoal()) return; 43 | if (cell.resource.type === GAME_CONSTANTS.RESOURCE_TYPES.URANIUM && !player.researchedUranium()) return; 44 | const dist = cell.pos.distanceTo(unit.pos); 45 | if (dist < closestDist) { 46 | closestDist = dist; 47 | closestResourceTile = cell; 48 | } 49 | }) 50 | if (closestResourceTile != null) { 51 | const dir = unit.pos.directionTo(closestResourceTile.pos); 52 | // move the unit in the direction towards the closest resource tile's position. 53 | actions.push(unit.move(dir)); 54 | } 55 | } else { 56 | // if unit is a worker and there is no cargo space left, and we have cities, lets return to them 57 | if (player.cities.size > 0) { 58 | const city = player.cities.values().next().value 59 | let closestDist = 999999; 60 | let closestCityTile = null; 61 | 62 | city.citytiles.forEach((citytile) => { 63 | const dist = citytile.pos.distanceTo(unit.pos); 64 | if (dist < closestDist) { 65 | closestCityTile = citytile; 66 | closestDist = dist; 67 | } 68 | }); 69 | if (closestCityTile != null) { 70 | const dir = unit.pos.directionTo(closestCityTile.pos); 71 | actions.push(unit.move(dir)); 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | // you can add debug annotations using the functions in the annotate object 79 | // actions.push(annotate.circle(0, 0)) 80 | 81 | /** AI Code Goes Above! **/ 82 | 83 | /** Do not edit! **/ 84 | console.log(actions.join(",")); 85 | // end turn 86 | agent.endTurn(); 87 | } 88 | }); 89 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/main.py: -------------------------------------------------------------------------------- 1 | from subprocess import Popen, PIPE 2 | from threading import Thread 3 | from queue import Queue, Empty 4 | 5 | import atexit 6 | import os 7 | import sys 8 | agent_processes = [None, None] 9 | t = None 10 | q = None 11 | def cleanup_process(): 12 | global agent_processes 13 | for proc in agent_processes: 14 | if proc is not None: 15 | proc.kill() 16 | def enqueue_output(out, queue): 17 | for line in iter(out.readline, b''): 18 | queue.put(line) 19 | out.close() 20 | def js_agent(observation, configuration): 21 | """ 22 | a wrapper around a js agent 23 | """ 24 | global agent_processes, t, q 25 | 26 | agent_process = agent_processes[observation.player] 27 | ### Do not edit ### 28 | if agent_process is None: 29 | if "__raw_path__" in configuration: 30 | cwd = os.path.dirname(configuration["__raw_path__"]) 31 | else: 32 | cwd = os.path.dirname(__file__) 33 | agent_process = Popen(["node", "main.js"], stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=cwd) 34 | agent_processes[observation.player] = agent_process 35 | atexit.register(cleanup_process) 36 | 37 | # following 4 lines from https://stackoverflow.com/questions/375427/a-non-blocking-read-on-a-subprocess-pipe-in-python 38 | q = Queue() 39 | t = Thread(target=enqueue_output, args=(agent_process.stderr, q)) 40 | t.daemon = True # thread dies with the program 41 | t.start() 42 | if observation.step == 0: 43 | # fixes bug where updates array is shared, but the first update is agent dependent actually 44 | observation["updates"][0] = f"{observation.player}" 45 | 46 | # print observations to agent 47 | agent_process.stdin.write(("\n".join(observation["updates"]) + "\n").encode()) 48 | agent_process.stdin.flush() 49 | 50 | # wait for data written to stdout 51 | agent1res = (agent_process.stdout.readline()).decode() 52 | _end_res = (agent_process.stdout.readline()).decode() 53 | 54 | while True: 55 | try: line = q.get_nowait() 56 | except Empty: 57 | # no standard error received, break 58 | break 59 | else: 60 | # standard error output received, print it out 61 | print(line.decode(), file=sys.stderr, end='') 62 | 63 | outputs = agent1res.split("\n")[0].split(",") 64 | actions = [] 65 | for cmd in outputs: 66 | if cmd != "": 67 | actions.append(cmd) 68 | return actions -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/simple.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/simple.tar.gz -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/annotate.py: -------------------------------------------------------------------------------- 1 | def circle(x: int, y: int) -> str: 2 | return f"dc {x} {y}" 3 | 4 | def x(x: int, y: int) -> str: 5 | return f"dx {x} {y}" 6 | 7 | def line(x1: int, y1: int, x2: int, y2: int) -> str: 8 | return f"dl {x1} {y1} {x2} {y2}" 9 | 10 | # text at cell on map 11 | def text(x: int, y: int, message: str, fontsize: int = 16) -> str: 12 | return f"dt {x} {y} {fontsize} '{message}'" 13 | 14 | # text besides map 15 | def sidetext(message: str) -> str: 16 | return f"dst '{message}'" 17 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/constants.py: -------------------------------------------------------------------------------- 1 | class Constants: 2 | class INPUT_CONSTANTS: 3 | RESEARCH_POINTS = "rp" 4 | RESOURCES = "r" 5 | UNITS = "u" 6 | CITY = "c" 7 | CITY_TILES = "ct" 8 | ROADS = "ccd" 9 | DONE = "D_DONE" 10 | class DIRECTIONS: 11 | NORTH = "n" 12 | WEST = "w" 13 | SOUTH = "s" 14 | EAST = "e" 15 | CENTER = "c" 16 | class UNIT_TYPES: 17 | WORKER = 0 18 | CART = 1 19 | class RESOURCE_TYPES: 20 | WOOD = "wood" 21 | URANIUM = "uranium" 22 | COAL = "coal" 23 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game.py: -------------------------------------------------------------------------------- 1 | from .constants import Constants 2 | from .game_map import GameMap 3 | from .game_objects import Player, Unit, City, CityTile 4 | 5 | INPUT_CONSTANTS = Constants.INPUT_CONSTANTS 6 | 7 | 8 | class Game: 9 | def _initialize(self, messages): 10 | """ 11 | initialize state 12 | """ 13 | self.id = int(messages[0]) 14 | self.turn = -1 15 | # get some other necessary initial input 16 | mapInfo = messages[1].split(" ") 17 | self.map_width = int(mapInfo[0]) 18 | self.map_height = int(mapInfo[1]) 19 | self.map = GameMap(self.map_width, self.map_height) 20 | self.players = [Player(0), Player(1)] 21 | 22 | def _end_turn(self): 23 | print("D_FINISH") 24 | 25 | def _reset_player_states(self): 26 | self.players[0].units = [] 27 | self.players[0].cities = {} 28 | self.players[0].city_tile_count = 0 29 | self.players[1].units = [] 30 | self.players[1].cities = {} 31 | self.players[1].city_tile_count = 0 32 | 33 | def _update(self, messages): 34 | """ 35 | update state 36 | """ 37 | self.map = GameMap(self.map_width, self.map_height) 38 | self.turn += 1 39 | self._reset_player_states() 40 | 41 | for update in messages: 42 | if update == "D_DONE": 43 | break 44 | strs = update.split(" ") 45 | input_identifier = strs[0] 46 | if input_identifier == INPUT_CONSTANTS.RESEARCH_POINTS: 47 | team = int(strs[1]) 48 | self.players[team].research_points = int(strs[2]) 49 | elif input_identifier == INPUT_CONSTANTS.RESOURCES: 50 | r_type = strs[1] 51 | x = int(strs[2]) 52 | y = int(strs[3]) 53 | amt = int(float(strs[4])) 54 | self.map._setResource(r_type, x, y, amt) 55 | elif input_identifier == INPUT_CONSTANTS.UNITS: 56 | unittype = int(strs[1]) 57 | team = int(strs[2]) 58 | unitid = strs[3] 59 | x = int(strs[4]) 60 | y = int(strs[5]) 61 | cooldown = float(strs[6]) 62 | wood = int(strs[7]) 63 | coal = int(strs[8]) 64 | uranium = int(strs[9]) 65 | self.players[team].units.append(Unit(team, unittype, unitid, x, y, cooldown, wood, coal, uranium)) 66 | elif input_identifier == INPUT_CONSTANTS.CITY: 67 | team = int(strs[1]) 68 | cityid = strs[2] 69 | fuel = float(strs[3]) 70 | lightupkeep = float(strs[4]) 71 | self.players[team].cities[cityid] = City(team, cityid, fuel, lightupkeep) 72 | elif input_identifier == INPUT_CONSTANTS.CITY_TILES: 73 | team = int(strs[1]) 74 | cityid = strs[2] 75 | x = int(strs[3]) 76 | y = int(strs[4]) 77 | cooldown = float(strs[5]) 78 | city = self.players[team].cities[cityid] 79 | citytile = city._add_city_tile(x, y, cooldown) 80 | self.map.get_cell(x, y).citytile = citytile 81 | self.players[team].city_tile_count += 1; 82 | elif input_identifier == INPUT_CONSTANTS.ROADS: 83 | x = int(strs[1]) 84 | y = int(strs[2]) 85 | road = float(strs[3]) 86 | self.map.get_cell(x, y).road = road 87 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_constants.json: -------------------------------------------------------------------------------- 1 | { 2 | "UNIT_TYPES": { 3 | "WORKER": 0, 4 | "CART": 1 5 | }, 6 | "RESOURCE_TYPES": { 7 | "WOOD": "wood", 8 | "COAL": "coal", 9 | "URANIUM": "uranium" 10 | }, 11 | "DIRECTIONS": { 12 | "NORTH": "n", 13 | "WEST": "w", 14 | "EAST": "e", 15 | "SOUTH": "s", 16 | "CENTER": "c" 17 | }, 18 | "PARAMETERS": { 19 | "DAY_LENGTH": 30, 20 | "NIGHT_LENGTH": 10, 21 | "MAX_DAYS": 360, 22 | "LIGHT_UPKEEP": { 23 | "CITY": 30, 24 | "WORKER": 4, 25 | "CART": 10 26 | }, 27 | "WOOD_GROWTH_RATE": 1.01, 28 | "MAX_WOOD_AMOUNT": 500, 29 | "CITY_BUILD_COST": 100, 30 | "CITY_ADJACENCY_BONUS": 5, 31 | "RESOURCE_CAPACITY": { 32 | "WORKER": 100, 33 | "CART": 2000 34 | }, 35 | "WORKER_COLLECTION_RATE": { 36 | "WOOD": 20, 37 | "COAL": 5, 38 | "URANIUM": 2 39 | }, 40 | "RESOURCE_TO_FUEL_RATE": { 41 | "WOOD": 1, 42 | "COAL": 10, 43 | "URANIUM": 40 44 | }, 45 | "RESEARCH_REQUIREMENTS": { 46 | "COAL": 50, 47 | "URANIUM": 200 48 | }, 49 | "CITY_ACTION_COOLDOWN": 10, 50 | "UNIT_ACTION_COOLDOWN": { 51 | "CART": 3, 52 | "WORKER": 2 53 | }, 54 | "MAX_ROAD": 6, 55 | "MIN_ROAD": 0, 56 | "CART_ROAD_DEVELOPMENT_RATE": 0.5, 57 | "PILLAGE_RATE": 0.5 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_constants.py: -------------------------------------------------------------------------------- 1 | import json 2 | from os import path 3 | dir_path = path.dirname(__file__) 4 | constants_path = path.abspath(path.join(dir_path, "game_constants.json")) 5 | with open(constants_path) as f: 6 | GAME_CONSTANTS = json.load(f) -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_agents/python/random_agent.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import random 3 | if __package__ == "": 4 | # for kaggle-environments 5 | from lux.game import Game 6 | from lux.game_map import Cell, RESOURCE_TYPES 7 | from lux.constants import Constants 8 | from lux.game_constants import GAME_CONSTANTS 9 | from lux import annotate 10 | else: 11 | # for CLI tool 12 | from .lux.game import Game 13 | from .lux.game_map import Cell, RESOURCE_TYPES 14 | from .lux.constants import Constants 15 | from .lux.game_constants import GAME_CONSTANTS 16 | from .lux import annotate 17 | DIRECTIONS = Constants.DIRECTIONS 18 | game_state = None 19 | 20 | def random_agent(observation, configuration): 21 | """ 22 | a blank, completely empty agent, usually incapable of surviving past the first night 23 | """ 24 | global game_state 25 | 26 | ### Do not edit ### 27 | if observation["step"] == 0: 28 | game_state = Game() 29 | game_state._initialize(observation["updates"]) 30 | game_state._update(observation["updates"][2:]) 31 | else: 32 | game_state._update(observation["updates"]) 33 | 34 | actions = [] 35 | 36 | ### AI Code goes down here! ### 37 | player = game_state.players[observation.player] 38 | for unit in player.units: 39 | dirs = [DIRECTIONS.NORTH, DIRECTIONS.WEST, DIRECTIONS.EAST, DIRECTIONS.SOUTH] 40 | action = unit.move(random.choice(dirs)) 41 | actions.append(action) 42 | 43 | return actions -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/test_lux.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from kaggle_environments import make 3 | from .agents import random_agent, js_simple_agent, simple_agent 4 | 5 | def test_lux_completes(): 6 | env = make("lux_ai_2021", configuration={}) 7 | env.run([random_agent, simple_agent]) 8 | json = env.toJSON() 9 | assert json["name"] == "lux_ai_2021" 10 | assert json["statuses"] == ["DONE", "DONE"] 11 | 12 | def test_js_agents(): 13 | env = make("lux_ai_2021", configuration={}) 14 | env.run([simple_agent, js_simple_agent]) 15 | json = env.toJSON() 16 | assert json["name"] == "lux_ai_2021" 17 | assert json["statuses"] == ["DONE", "DONE"] -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/testing.md: -------------------------------------------------------------------------------- 1 | Unfortunately at this moment, there isn't a progammatic way to test if the Kaggle Engine is the exact same as the engine competitors use when local testing. This is part of a TODO to make kaggle replays match the local replays players generate when they develop locally using https://github.com/Lux-AI-Challenge/Lux-Design-2021 2 | 3 | The following steps work for now: 4 | 5 | First download the simple kit from https://github.com/Lux-AI-Challenge/Lux-Design-2021/tree/master/kits/python (or another language) or use your own bot 6 | 7 | First run a game in Kaggle like so: 8 | 9 | ``` 10 | kaggle-environments run --environment lux_ai_2021 --agents path/to/bot/main.py path/to/bot/main.py --render '{"mode": "json"}' --out out.json --debug=True 11 | ``` 12 | 13 | Then upload the out.json to https://2021vis.lux-ai.org/ and go to the final turn. 14 | 15 | Then install the local engine `npm i -g install @lux-ai/2021-challenge` if you haven't done so already (you can remove `-g` and do `npx lux-ai-2021` as opposed to `lux-ai-2021`) 16 | 17 | Then run 18 | 19 | ``` 20 | lux-ai-2021 path/to/bot/main.py path/to/bot/main.py --out=replay.json 21 | ``` 22 | 23 | upload `replay.json` to the replay viewer again in a new tab and compare the final state with the other kaggle produced match. If the statistics and layout of units on the map match, then the engine is working correctly. (Given the complexity of the game and that `lux-ai-2021` generates action based replays, should a single thing be wrong, the differences in the final turn would be fairly massive so this is generally a sufficient test) -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_2021/todo.md.og: -------------------------------------------------------------------------------- 1 | # TODOs 2 | 3 | 4 | [x] Setup Visualizer 5 | 6 | [x] Keep dimensions engine results consistent with kaggle-env integated version 7 | 8 | [ ] Verify rewards work correctly 9 | 10 | [ ] display correct agent names 11 | 12 | [ ] default turn debug mode off 13 | 14 | [ ] add tests for more bots 15 | 16 | [ ] allow user to pass in custom configurations 17 | 18 | [ ] allow user to specify debug mode level for the dimensions engine -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/README.md: -------------------------------------------------------------------------------- 1 | # Lux AI Challenge Season 2 2 | 3 | Welcome to the Kaggle Environments wrapped around the Lux AI Challenge Season 2! For more information or if you have an issues or want to make contributes, check out the main repository at https://github.com/Lux-AI-Challenge/Lux-Design-2022 4 | 5 | ## Test scripts 6 | 7 | ``` 8 | kaggle-environments run --environment lux_ai_2022 --agents path/to/bot/main.py path/to/bot/main.py --render '{"mode": "json"}' --out out.json --debug=True 9 | ``` 10 | 11 | ``` 12 | kaggle-environments run --environment lux_ai_2022 --agents ../Lux-Design-2022/kits/js/main.py ../Lux-Design-2022/kits/js/main.py --render '{"mode": "json"}' --out out.json --debug=True 13 | ``` 14 | 15 | ## Packaging external packages locally 16 | 17 | git clone open_simplex and petting zoo directly and move them into here 18 | 19 | ## Visualizer 20 | 21 | Ensure that the script module is at the bottom of tag. -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_s2/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/agents.py: -------------------------------------------------------------------------------- 1 | from .test_agents.python.main import agent_fn as random_agent 2 | all_agents = { 3 | "random_agent": random_agent, 4 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Lux Eye S2 9 | 10 | 11 | 35 | 36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/lux_ai_s2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lux_ai_s2", 3 | "title": "Lux AI Challenge Season 2", 4 | "description": "A Novel AI Programming Challenge about Lux", 5 | "version": "2.1.7", 6 | "agents": [2], 7 | "configuration": { 8 | "episodeSteps": { 9 | "description": "Maximum number of steps the environment can run. Total is this number -1. One complete game is 1000 + N * 2 + 1 steps where N is number of factories each player is given.", 10 | "type": "integer", 11 | "minimum": 2, 12 | "default": 1020 13 | }, 14 | "max_episode_length": { 15 | "description": "Max game steps the environment can run, not including the early phase of the game.", 16 | "minimum": 2, 17 | "type": "integer", 18 | "default": 1000 19 | }, 20 | "seed": { 21 | "description": "Seed to use for episodes", 22 | "type": "integer" 23 | }, 24 | "actTimeout": 3, 25 | "runTimeout": 9600, 26 | "env_cfg": { 27 | "type": "object" 28 | } 29 | }, 30 | "reward": { 31 | "description": "Reward of the agent. Equal to amount of lichen grown.", 32 | "type": "integer", 33 | "default": 0 34 | }, 35 | "observation": { 36 | "remainingOverageTime": 60, 37 | "width": { 38 | "description": "The width of the map", 39 | "type": "integer", 40 | "shared": true, 41 | "default": 48 42 | }, 43 | "height": { 44 | "description": "The height of the map", 45 | "type": "integer", 46 | "shared": true, 47 | "default": 48 48 | }, 49 | "reward": { 50 | "description": "Current reward of the agent. Equal to amount of lichen grown.", 51 | "type": "integer", 52 | "default": 0 53 | }, 54 | "obs": { 55 | "description": "String containing the observations", 56 | "type": "string", 57 | "shared": true 58 | }, 59 | "player": { 60 | "description": "Current player's index / team id", 61 | "type": "string", 62 | "defaults": "player_0" 63 | }, 64 | "stats": { 65 | "description": "Aggregate statistics for an episode", 66 | "type": "object" 67 | } 68 | }, 69 | "action": { 70 | "description": "Actions", 71 | "type": "object" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_s2/luxai_s2/.DS_Store -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/__init__.py: -------------------------------------------------------------------------------- 1 | from .env import LuxAI_S2 2 | from .version import __version__ 3 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/globals.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | TERM_COLORS = True 4 | try: 5 | from termcolor import colored 6 | 7 | TERM_COLORS = ( 8 | os.environ["LUX_COLORS"] == "False" if "LUX_COLORS" in os.environ else True 9 | ) 10 | except: 11 | TERM_COLORS = False 12 | print("termcolor not installed, skipping dependency") 13 | pass 14 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/map/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_s2/luxai_s2/map/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/map/bfs_deltas_gen.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | move_deltas = np.array([[0, 0], [0, -1], [1, 0], [0, 1], [-1, 0]]) 4 | 5 | 6 | def gen_deltas(size=3): 7 | start = (0, 0) 8 | f = [start] 9 | seen = set() 10 | r = set() 11 | while len(f) > 0: 12 | pos = f.pop(0) 13 | seen.add(pos) 14 | if abs(pos[0]) + abs(pos[1]) > size: 15 | break 16 | r.add(pos) 17 | for md in move_deltas: 18 | new_pos = (pos[0] + md[0], pos[1] + md[1]) 19 | if new_pos not in seen: 20 | f.append(new_pos) 21 | print(r) 22 | 23 | 24 | gen_deltas(6) 25 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/map/position.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class Position: 5 | def __init__(self, pos: np.ndarray) -> None: 6 | self.pos: np.ndarray = pos 7 | 8 | @property 9 | def x(self) -> int: 10 | return self.pos[0] 11 | 12 | @property 13 | def y(self) -> int: 14 | return self.pos[1] 15 | 16 | def __add__(self, o): 17 | if isinstance(o, Position): 18 | return Position(pos=self.pos + o.pos) 19 | else: 20 | return Position(pos=self.pos + o) 21 | 22 | def __sub__(self, o): 23 | if isinstance(o, Position): 24 | return Position(pos=self.pos - o.pos) 25 | else: 26 | return Position(pos=self.pos - o) 27 | 28 | def __str__(self) -> str: 29 | return f"({self.x}, {self.y})" 30 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/map_generator/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_s2/luxai_s2/map_generator/.DS_Store -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/map_generator/__init__.py: -------------------------------------------------------------------------------- 1 | from .generator import GameMap 2 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/map_generator/timing.py: -------------------------------------------------------------------------------- 1 | import timeit 2 | 3 | N = 100 4 | for map_type in ["Cave", "Craters", "Island", "Mountain"]: 5 | time = timeit.timeit( 6 | f"{map_type}(64, 64)", setup=f"from generator import {map_type}", number=N 7 | ) 8 | print(f"Took {time / N * 1000:.2f}ms for {map_type}.") 9 | 10 | 11 | # If you want to use cProfile, you can look in git history. 12 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/map_generator/visualize.py: -------------------------------------------------------------------------------- 1 | # A quick visualizer to aid in map generating algorithms. 2 | try: 3 | import pygame 4 | except: 5 | pass 6 | import numpy as np 7 | 8 | 9 | def viz(game_map, screen=None): 10 | N = min(500 // game_map.width, 750 // game_map.height) 11 | if screen is None: 12 | screen = pygame.display.set_mode((3 * N * game_map.width, N * game_map.height)) 13 | 14 | for x in range(game_map.width): 15 | for y in range(game_map.height): 16 | rubble_color = [255 - game_map.rubble[y][x] * 255 / 100] * 3 17 | ice_color = [0, 0, game_map.ice[y][x] * 255 / 100] 18 | ore_color = [game_map.ore[y][x] * 255 / 100, 0, 0] 19 | screen.fill(rubble_color, (N * x, N * y, N, N)) 20 | screen.fill(ice_color, (N * x + N * game_map.width, N * y, N, N)) 21 | screen.fill(ore_color, (N * x + 2 * N * game_map.width, N * y, N, N)) 22 | pygame.display.update() 23 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/map_generator/visualize_samples.py: -------------------------------------------------------------------------------- 1 | from math import prod 2 | from pathlib import Path 3 | 4 | import numpy as np 5 | import pygame 6 | 7 | from luxai_s2.env import LuxAI_S2 8 | 9 | if __name__ == "__main__": 10 | env = LuxAI_S2() 11 | N = 100 12 | Path("./map_samples").mkdir(parents=True, exist_ok=True) 13 | stats = dict(rubble_count=[], ice=[], ore=[]) 14 | for seed in range(10000, 10000 + N): 15 | env.reset(seed) 16 | env.render() 17 | env.py_visualizer.update_scene(env.state) 18 | 19 | pygame.image.save(env.py_visualizer.screen, f"map_samples/{seed}.jpg") 20 | stats["ice"] += [env.state.board.ice.sum()] 21 | stats["ore"] += [env.state.board.ore.sum()] 22 | stats["rubble_count"] += [env.state.board.rubble.sum()] 23 | 24 | for k in stats: 25 | stats[k] = np.mean(stats[k]) 26 | stats["rubble_count"] /= prod(env.state.board.rubble.shape) 27 | print(stats) 28 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/pyvisual/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_s2/luxai_s2/pyvisual/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/replay/__init__.py: -------------------------------------------------------------------------------- 1 | from .replay import decode_replay_file, generate_replay 2 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/replay/replay.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import os.path as osp 4 | from typing import List, Tuple 5 | 6 | from luxai_s2.state import State 7 | 8 | 9 | def decode_replay_file(replay_file) -> Tuple[State, List]: 10 | """ 11 | Takes an input replay file of any kind from any source and extracts the full trajectory with observations 12 | or just initial state and actions if observations aren't there 13 | """ 14 | ext = osp.splitext(replay_file)[-1] 15 | if ext == ".json": 16 | # probably kaggle replay 17 | with open(replay_file, "r") as f: 18 | replay = json.load(f) 19 | init_state = replay["init_state"] 20 | pass 21 | elif ext == ".h5": 22 | pass 23 | 24 | 25 | def generate_replay(states: List[State]): 26 | """ 27 | Generates a compressed replay. 28 | 29 | """ 30 | return [s.get_compressed_obs() for s in states] 31 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/spaces/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_s2/luxai_s2/spaces/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/state/__init__.py: -------------------------------------------------------------------------------- 1 | from .state import DeltaObservationStateDict, ObservationStateDict, State 2 | from .stats import StatsStateDict, create_empty_stats 3 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/state/typing.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_s2/luxai_s2/state/typing.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/team.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from enum import Enum 3 | from typing import List 4 | try: 5 | from typing import TypedDict 6 | except: 7 | from typing_extensions import TypedDict 8 | 9 | from luxai_s2.config import EnvConfig 10 | from luxai_s2.globals import TERM_COLORS 11 | 12 | try: 13 | from termcolor import colored 14 | except: 15 | pass 16 | 17 | 18 | @dataclass 19 | class FactionInfo: 20 | color: str = "none" 21 | alt_color: str = "red" 22 | faction_id: int = -1 23 | 24 | 25 | class FactionTypes(Enum): 26 | AlphaStrike = FactionInfo(color="yellow", faction_id=0) 27 | MotherMars = FactionInfo(color="green", faction_id=1) 28 | TheBuilders = FactionInfo(color="blue", faction_id=2) 29 | FirstMars = FactionInfo(color="red", faction_id=3) 30 | 31 | 32 | class TeamStateDict(TypedDict): 33 | team_id: int 34 | faction: str 35 | water: int 36 | metal: int 37 | factories_to_place: int 38 | factory_strains: List[int] 39 | place_first: bool 40 | bid: int 41 | 42 | 43 | class Team: 44 | def __init__(self, team_id: int, agent: str, faction: FactionTypes = None) -> None: 45 | self.faction = faction 46 | self.team_id = team_id 47 | # the key used to differentiate ownership of things in state 48 | self.agent = agent 49 | 50 | self.init_water = 0 51 | self.init_metal = 0 52 | self.factories_to_place = 0 53 | self.factory_strains = [] 54 | 55 | # whether this team gets to place factories down first or not. The bid winner has this set to True. 56 | # If tied, player_0's team has this True 57 | self.place_first = False 58 | 59 | self.bid = 0 60 | 61 | def state_dict(self) -> TeamStateDict: 62 | return dict( 63 | team_id=self.team_id, 64 | faction=self.faction.name, 65 | # note for optimization, water,metal, factories_to_place doesn't change after the early game. 66 | water=self.init_water, 67 | metal=self.init_metal, 68 | factories_to_place=self.factories_to_place, 69 | factory_strains=self.factory_strains, 70 | place_first=self.place_first, 71 | bid=self.bid, 72 | ) 73 | 74 | def __str__(self) -> str: 75 | out = f"[Player {self.team_id}]" 76 | if TERM_COLORS: 77 | return colored(out, self.faction.value.color) 78 | return out 79 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_s2/luxai_s2/tools/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/tools/replay.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path as osp 3 | from typing import List 4 | 5 | from luxai_s2.replay import decode_replay_file 6 | from luxai_s2.state import State 7 | 8 | 9 | def replay_trajectory(replay_file: str): 10 | decoded = decode_replay_file(replay_file) 11 | if "observations" in decoded: 12 | print("replay file already contains observations, there is nothing to do") 13 | return replay_file 14 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .animate import animate 2 | from .utils import is_day, my_turn_to_place_factory 3 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/utils/animate.py: -------------------------------------------------------------------------------- 1 | def animate(imgs, _return=True, fps=10, filename="__temp__.mp4"): 2 | from moviepy.editor import ImageSequenceClip 3 | 4 | clip = ImageSequenceClip(imgs, fps=fps) 5 | clip.write_videofile(filename, verbose=False, logger=None) 6 | if _return: 7 | from IPython.display import Video 8 | 9 | return Video(filename) 10 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/utils/heuristics/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/utils/heuristics/bidding.py: -------------------------------------------------------------------------------- 1 | from luxai_s2.unit import BidActionType 2 | from luxai_s2.state import ObservationStateDict 3 | def zero_bid(player, obs: ObservationStateDict) -> BidActionType: 4 | faction = "AlphaStrike" 5 | if player == "player_1": 6 | faction = "MotherMars" 7 | return dict(bid=0, faction=faction) -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/utils/heuristics/factory.py: -------------------------------------------------------------------------------- 1 | from luxai_s2.state import ObservationStateDict 2 | 3 | 4 | def build_single_heavy(agent, obs: ObservationStateDict): 5 | actions = dict() 6 | if agent == "player_0": 7 | factories = obs["factories"][agent] 8 | units = obs["units"][agent] 9 | if len(units) == 0: 10 | for unit_id in factories: 11 | factory = factories[unit_id] 12 | # if factory["cargo"]["metal"] >= 100: 13 | actions[unit_id] = 1 # build a heavy 14 | 15 | return actions 16 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/utils/heuristics/factory_placement.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from luxai_s2.unit import FactoryPlacementActionType 3 | from luxai_s2.state import ObservationStateDict 4 | 5 | def random_factory_placement(player, obs: ObservationStateDict) -> FactoryPlacementActionType: 6 | """ 7 | This policy places factories with 150 water and metal at random locations 8 | """ 9 | # we will spawn our factory in a random location with 150 metal and water if it is our turn to place 10 | potential_spawns = np.array(list(zip(*np.where(obs["board"]["valid_spawns_mask"] == 1)))) 11 | spawn_loc = potential_spawns[np.random.randint(0, len(potential_spawns))] 12 | return dict(spawn=spawn_loc, metal=150, water=150) 13 | 14 | def place_near_random_ice(player, obs: ObservationStateDict): 15 | if obs["teams"][player]["metal"] == 0: 16 | return dict() 17 | potential_spawns = list(zip(*np.where(obs["board"]["valid_spawns_mask"] == 1))) 18 | potential_spawns_set = set(potential_spawns) 19 | done_search = False 20 | # if player == "player_1": 21 | ice_diff = np.diff(obs["board"]["ice"]) 22 | pot_ice_spots = np.argwhere(ice_diff == 1) 23 | if len(pot_ice_spots) == 0: 24 | pot_ice_spots = potential_spawns 25 | trials = 5 26 | while trials > 0: 27 | pos_idx = np.random.randint(0, len(pot_ice_spots)) 28 | pos = pot_ice_spots[pos_idx] 29 | 30 | area = 3 31 | for x in range(area): 32 | for y in range(area): 33 | check_pos = [pos[0] + x - area // 2, pos[1] + y - area // 2] 34 | if tuple(check_pos) in potential_spawns_set: 35 | done_search = True 36 | pos = check_pos 37 | break 38 | if done_search: 39 | break 40 | if done_search: 41 | break 42 | trials -= 1 43 | spawn_loc = potential_spawns[np.random.randint(0, len(potential_spawns))] 44 | if not done_search: 45 | pos = spawn_loc 46 | 47 | metal = obs["teams"][player]["metal"] 48 | return dict(spawn=pos, metal=metal, water=metal) 49 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/utils/utils.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from luxai_s2.config import EnvConfig 4 | from luxai_s2.unit import Unit, UnitType 5 | 6 | 7 | def is_day(config: EnvConfig, env_step): 8 | return env_step % config.CYCLE_LENGTH < config.DAY_LENGTH 9 | 10 | 11 | def get_top_two_power_units(units: List[Unit], unit_type: UnitType): 12 | most_power_unit: Unit = units[0] 13 | most_power = -1 14 | next_most_power_unit: Unit = units[1] 15 | next_most_power = -1 16 | for u in units: 17 | if u.unit_type == unit_type: 18 | if u.power > most_power: 19 | next_most_power_unit = most_power_unit 20 | most_power_unit = u 21 | most_power = u.power 22 | elif ( 23 | u.power >= next_most_power 24 | ): # >= check since we want to top 2 power units which can tie 25 | next_most_power_unit = u 26 | next_most_power = u.power 27 | 28 | return (most_power_unit, next_most_power_unit) 29 | 30 | 31 | def my_turn_to_place_factory(place_first: bool, step: int): 32 | if place_first: 33 | if step % 2 == 1: 34 | return True 35 | else: 36 | if step % 2 == 0: 37 | return True 38 | return False 39 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "" -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/wrappers/__init__.py: -------------------------------------------------------------------------------- 1 | from .controllers import Controller 2 | from .sb3 import SB3Wrapper 3 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/luxai_s2/wrappers/controllers.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any 2 | 3 | import numpy.typing as npt 4 | from gymnasium import spaces 5 | 6 | class Controller: 7 | def __init__(self, action_space: spaces.Space) -> None: 8 | self.action_space = action_space 9 | 10 | def action_to_lux_action( 11 | self, agent: str, obs: Dict[str, Any], action: npt.NDArray 12 | ): 13 | """ 14 | Takes as input the current "raw observation" and the parameterized action and returns 15 | an action formatted for the Lux env 16 | """ 17 | raise NotImplementedError() 18 | def action_masks(self, agent: str, obs: Dict[str, Any]): 19 | """ 20 | Generates a boolean action mask indicating in each discrete dimension whether it would be valid or not 21 | """ 22 | raise NotImplementedError() 23 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/test_agents/python/lux/cargo.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | @dataclass 4 | class UnitCargo: 5 | ice: int = 0 6 | ore: int = 0 7 | water: int = 0 8 | metal: int = 0 9 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/test_agents/python/lux/factory.py: -------------------------------------------------------------------------------- 1 | import math 2 | from sys import stderr 3 | import numpy as np 4 | from dataclasses import dataclass 5 | if __package__ == "": 6 | from lux.cargo import UnitCargo 7 | from lux.config import EnvConfig 8 | else: 9 | from .cargo import UnitCargo 10 | from .config import EnvConfig 11 | @dataclass 12 | class Factory: 13 | team_id: int 14 | unit_id: str 15 | strain_id: int 16 | power: int 17 | cargo: UnitCargo 18 | pos: np.ndarray 19 | # lichen tiles connected to this factory 20 | # lichen_tiles: np.ndarray 21 | env_cfg: EnvConfig 22 | 23 | def build_heavy_metal_cost(self, game_state): 24 | unit_cfg = self.env_cfg.ROBOTS["HEAVY"] 25 | return unit_cfg.METAL_COST 26 | def build_heavy_power_cost(self, game_state): 27 | unit_cfg = self.env_cfg.ROBOTS["HEAVY"] 28 | return unit_cfg.POWER_COST 29 | def can_build_heavy(self, game_state): 30 | return self.power >= self.build_heavy_power_cost(game_state) and self.cargo.metal >= self.build_heavy_metal_cost(game_state) 31 | def build_heavy(self): 32 | return 1 33 | 34 | def build_light_metal_cost(self, game_state): 35 | unit_cfg = self.env_cfg.ROBOTS["LIGHT"] 36 | return unit_cfg.METAL_COST 37 | def build_light_power_cost(self, game_state): 38 | unit_cfg = self.env_cfg.ROBOTS["LIGHT"] 39 | return unit_cfg.POWER_COST 40 | def can_build_light(self, game_state): 41 | return self.power >= self.build_light_power_cost(game_state) and self.cargo.metal >= self.build_light_metal_cost(game_state) 42 | 43 | def build_light(self): 44 | return 0 45 | 46 | def water_cost(self, game_state): 47 | """ 48 | Water required to perform water action 49 | """ 50 | owned_lichen_tiles = (game_state.board.lichen_strains == self.strain_id).sum() 51 | return np.ceil(owned_lichen_tiles / self.env_cfg.LICHEN_WATERING_COST_FACTOR) 52 | def can_water(self, game_state): 53 | return self.cargo.water >= self.water_cost(game_state) 54 | def water(self): 55 | return 2 56 | 57 | @property 58 | def pos_slice(self): 59 | return slice(self.pos[0] - 1, self.pos[0] + 2), slice(self.pos[1] - 1, self.pos[1] + 2) 60 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/test_agents/python/lux/team.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from enum import Enum 3 | if __package__ == "": 4 | from lux.config import EnvConfig 5 | else: 6 | from .config import EnvConfig 7 | TERM_COLORS = False 8 | try: 9 | from termcolor import colored 10 | TERM_COLORS=True 11 | except: 12 | pass 13 | @dataclass 14 | class FactionInfo: 15 | color: str = "none" 16 | alt_color: str = "red" 17 | faction_id: int = -1 18 | 19 | class FactionTypes(Enum): 20 | Null = FactionInfo(color="gray", faction_id=0) 21 | AlphaStrike = FactionInfo(color="yellow", faction_id=1) 22 | MotherMars = FactionInfo(color="green", faction_id=2) 23 | TheBuilders = FactionInfo(color="blue", faction_id=3) 24 | FirstMars = FactionInfo(color="red", faction_id=4) 25 | 26 | class Team: 27 | def __init__(self, team_id: int, agent: str, faction: FactionTypes = None, water=0, metal=0, factories_to_place=0, factory_strains=[], place_first=False, bid=0) -> None: 28 | self.faction = faction 29 | self.team_id = team_id 30 | # the key used to differentiate ownership of things in state 31 | self.agent = agent 32 | 33 | self.water = water 34 | self.metal = metal 35 | self.factories_to_place = factories_to_place 36 | self.factory_strains = factory_strains 37 | # whether this team gets to place factories down first or not. The bid winner has this set to True. 38 | # If tied, player_0's team has this True 39 | self.place_first = place_first 40 | def state_dict(self): 41 | return dict( 42 | team_id=self.team_id, 43 | faction=self.faction.name, 44 | # note for optimization, water,metal, factories_to_place doesn't change after the early game. 45 | water=self.init_water, 46 | metal=self.init_metal, 47 | factories_to_place=self.factories_to_place, 48 | factory_strains=self.factory_strains, 49 | place_first=self.place_first, 50 | ) 51 | def __str__(self) -> str: 52 | out = f"[Player {self.team_id}]" 53 | if TERM_COLORS: 54 | return colored(out, self.faction.value.color) 55 | return out -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/test_agents/python/lux/unit.py: -------------------------------------------------------------------------------- 1 | import math 2 | import sys 3 | from typing import List 4 | import numpy as np 5 | from dataclasses import dataclass 6 | if __package__ == "": 7 | from lux.cargo import UnitCargo 8 | from lux.config import EnvConfig 9 | else: 10 | from .cargo import UnitCargo 11 | from .config import EnvConfig 12 | 13 | # a[1] = direction (0 = center, 1 = up, 2 = right, 3 = down, 4 = left) 14 | move_deltas = np.array([[0, 0], [0, -1], [1, 0], [0, 1], [-1, 0]]) 15 | 16 | @dataclass 17 | class Unit: 18 | team_id: int 19 | unit_id: str 20 | unit_type: str # "LIGHT" or "HEAVY" 21 | pos: np.ndarray 22 | power: int 23 | cargo: UnitCargo 24 | env_cfg: EnvConfig 25 | unit_cfg: dict 26 | action_queue: List 27 | 28 | @property 29 | def agent_id(self): 30 | if self.team_id == 0: return "player_0" 31 | return "player_1" 32 | 33 | def action_queue_cost(self, game_state): 34 | cost = self.env_cfg.ROBOTS[self.unit_type].ACTION_QUEUE_POWER_COST 35 | return cost 36 | 37 | def move_cost(self, game_state, direction): 38 | board = game_state.board 39 | target_pos = self.pos + move_deltas[direction] 40 | if target_pos[0] < 0 or target_pos[1] < 0 or target_pos[1] >= len(board.rubble) or target_pos[0] >= len(board.rubble[0]): 41 | # print("Warning, tried to get move cost for going off the map", file=sys.stderr) 42 | return None 43 | factory_there = board.factory_occupancy_map[target_pos[0], target_pos[1]] 44 | if factory_there not in game_state.teams[self.agent_id].factory_strains and factory_there != -1: 45 | # print("Warning, tried to get move cost for going onto a opposition factory", file=sys.stderr) 46 | return None 47 | rubble_at_target = board.rubble[target_pos[0]][target_pos[1]] 48 | 49 | return math.floor(self.unit_cfg.MOVE_COST + self.unit_cfg.RUBBLE_MOVEMENT_COST * rubble_at_target) 50 | def move(self, direction, repeat=0, n=1): 51 | if isinstance(direction, int): 52 | direction = direction 53 | else: 54 | pass 55 | return np.array([0, direction, 0, 0, repeat, n]) 56 | 57 | def transfer(self, transfer_direction, transfer_resource, transfer_amount, repeat=0, n=1): 58 | assert transfer_resource < 5 and transfer_resource >= 0 59 | assert transfer_direction < 5 and transfer_direction >= 0 60 | return np.array([1, transfer_direction, transfer_resource, transfer_amount, repeat, n]) 61 | 62 | def pickup(self, pickup_resource, pickup_amount, repeat=0, n=1): 63 | assert pickup_resource < 5 and pickup_resource >= 0 64 | return np.array([2, 0, pickup_resource, pickup_amount, repeat, n]) 65 | 66 | def dig_cost(self, game_state): 67 | return self.unit_cfg.DIG_COST 68 | def dig(self, repeat=0, n=1): 69 | return np.array([3, 0, 0, 0, repeat, n]) 70 | 71 | def self_destruct_cost(self, game_state): 72 | return self.unit_cfg.SELF_DESTRUCT_COST 73 | def self_destruct(self, repeat=0, n=1): 74 | return np.array([4, 0, 0, 0, repeat, n]) 75 | 76 | def recharge(self, x, repeat=0, n=1): 77 | return np.array([5, 0, 0, x, repeat, n]) 78 | 79 | def __str__(self) -> str: 80 | out = f"[{self.team_id}] {self.unit_id} {self.unit_type} at {self.pos}" 81 | return out -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/test_agents/python/lux/utils.py: -------------------------------------------------------------------------------- 1 | def my_turn_to_place_factory(place_first: bool, step: int): 2 | if place_first: 3 | if step % 2 == 1: 4 | return True 5 | else: 6 | if step % 2 == 0: 7 | return True 8 | return False 9 | 10 | # direction (0 = center, 1 = up, 2 = right, 3 = down, 4 = left) 11 | def direction_to(src, target): 12 | ds = target - src 13 | dx = ds[0] 14 | dy = ds[1] 15 | if dx == 0 and dy == 0: 16 | return 0 17 | if abs(dx) > abs(dy): 18 | if dx > 0: 19 | return 2 20 | else: 21 | return 4 22 | else: 23 | if dy > 0: 24 | return 3 25 | else: 26 | return 1 -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/test_agents/python/main.py: -------------------------------------------------------------------------------- 1 | import json 2 | from typing import Dict 3 | import sys 4 | from argparse import Namespace 5 | 6 | if __package__ == "": 7 | from agent import Agent 8 | from lux.config import EnvConfig 9 | from lux.kit import GameState, process_obs, to_json, from_json, process_action, obs_to_game_state 10 | else: 11 | from .agent import Agent 12 | from .lux.config import EnvConfig 13 | from .lux.kit import GameState, process_obs, to_json, from_json, process_action, obs_to_game_state 14 | 15 | ### DO NOT REMOVE THE FOLLOWING CODE ### 16 | agent_dict = dict() # store potentially multiple dictionaries as kaggle imports code directly 17 | agent_prev_obs = dict() 18 | def agent_fn(observation, configurations): 19 | """ 20 | agent definition for kaggle submission. 21 | """ 22 | global agent_dict 23 | step = observation.step 24 | 25 | 26 | player = observation.player 27 | remainingOverageTime = observation.remainingOverageTime 28 | if step == 0: 29 | env_cfg = EnvConfig.from_dict(configurations["env_cfg"]) 30 | agent_dict[player] = Agent(player, env_cfg) 31 | agent_prev_obs[player] = dict() 32 | agent = agent_dict[player] 33 | agent = agent_dict[player] 34 | obs = process_obs(player, agent_prev_obs[player], step, json.loads(observation.obs)) 35 | if step == 100: 36 | with open("obs.json", "w") as f: 37 | json.dump(to_json(obs), f) 38 | agent_prev_obs[player] = obs 39 | agent.step = step 40 | if obs["real_env_steps"] < 0: 41 | actions = agent.early_setup(step, obs, remainingOverageTime) 42 | else: 43 | actions = agent.act(step, obs, remainingOverageTime) 44 | 45 | return process_action(actions) 46 | 47 | if __name__ == "__main__": 48 | 49 | def read_input(): 50 | """ 51 | Reads input from stdin 52 | """ 53 | try: 54 | return input() 55 | except EOFError as eof: 56 | raise SystemExit(eof) 57 | step = 0 58 | player_id = 0 59 | configurations = None 60 | i = 0 61 | while True: 62 | inputs = read_input() 63 | obs = json.loads(inputs) 64 | 65 | observation = Namespace(**dict(step=obs["step"], obs=json.dumps(obs["obs"]), remainingOverageTime=obs["remainingOverageTime"], player=obs["player"], info=obs["info"])) 66 | if i == 0: 67 | configurations = obs["info"]["env_cfg"] 68 | i += 1 69 | actions = agent_fn(observation, dict(env_cfg=configurations)) 70 | # send actions to engine 71 | print(json.dumps(actions)) -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s2/test_lux.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from kaggle_environments import make 3 | from .agents import random_agent 4 | 5 | def test_lux_completes(): 6 | env = make("lux_ai_s2", debug=True) 7 | env.run([random_agent, random_agent]) 8 | json = env.toJSON() 9 | assert json["name"] == "lux_ai_s2" 10 | assert json["statuses"] == ["DONE", "DONE"] -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/README.md: -------------------------------------------------------------------------------- 1 | # Lux AI Challenge Season 3 2 | 3 | Welcome to the Kaggle Environments wrapped around the Lux AI Challenge Season 3! For more information or if you have an issues or want to make contributes, check out the main repository at https://github.com/Lux-AI-Challenge/Lux-Design-S3 4 | 5 | ## Test scripts 6 | 7 | ``` 8 | kaggle-environments run --environment lux_ai_s3 --agents path/to/bot/main.py path/to/bot/main.py --render '{"mode": "json"}' --out out.json --debug=True 9 | ``` 10 | 11 | ``` 12 | kaggle-environments run --environment lux_ai_s3 --agents ../LuxAI/Lux-Design-S3/kits/python/main.py ../LuxAI/Lux-Design-S3/kits/python/main.py --render '{"mode": "json"}' --out out.json --debug=True 13 | ``` 14 | 18 | 19 | ## Visualizer 20 | 21 | Ensure that the script module is at the bottom of tag. -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/agents.py: -------------------------------------------------------------------------------- 1 | from .test_agents.python.main import agent_fn as random_agent 2 | all_agents = { 3 | "random_agent": random_agent, 4 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Lux Eye S3 9 | 10 | 11 | 35 | 36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/lux_ai_s3.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lux_ai_s3", 3 | "title": "Lux AI Challenge Season 3", 4 | "description": "A Novel AI Programming Challenge about Lux", 5 | "version": "0.0.1", 6 | "agents": [2], 7 | "configuration": { 8 | "episodeSteps": 506, 9 | "seed": { 10 | "description": "Seed to use for episodes", 11 | "type": "integer" 12 | }, 13 | "actTimeout": 3, 14 | "runTimeout": 1600, 15 | "env_cfg": { 16 | "description": "Environment configuration. Not to be filled out by user as it is randomly generated.", 17 | "type": "object" 18 | } 19 | }, 20 | "reward": { 21 | "description": "Reward of the agent. Equal to number of games won.", 22 | "type": "integer", 23 | "default": 0 24 | }, 25 | "observation": { 26 | "remainingOverageTime": 60, 27 | "reward": { 28 | "description": "Current reward of the agent. Equal to amount of lichen grown.", 29 | "type": "integer", 30 | "default": 0 31 | }, 32 | "obs": { 33 | "description": "String containing the observations", 34 | "type": "string", 35 | "shared": false 36 | }, 37 | "player": { 38 | "description": "Current player's index / team id", 39 | "type": "string", 40 | "defaults": "player_0" 41 | } 42 | }, 43 | "action": { 44 | "description": "Actions", 45 | "type": "object" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/luxai_s3/__init__.py: -------------------------------------------------------------------------------- 1 | from .env import LuxAIS3Env 2 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/luxai_s3/globals.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | TERM_COLORS = True 4 | try: 5 | from termcolor import colored 6 | 7 | TERM_COLORS = ( 8 | os.environ["LUX_COLORS"] == "False" if "LUX_COLORS" in os.environ else True 9 | ) 10 | except: 11 | TERM_COLORS = False 12 | # print("termcolor not installed, skipping dependency") 13 | pass 14 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/luxai_s3/spaces.py: -------------------------------------------------------------------------------- 1 | import chex 2 | import jax 3 | import jax.numpy as jnp 4 | import numpy as np 5 | from gymnax.environments.spaces import Space 6 | 7 | 8 | class MultiDiscrete(Space): 9 | """Minimal jittable class for multi discrete gymnax spaces.""" 10 | 11 | def __init__(self, low: np.ndarray, high: np.ndarray): 12 | self.low = low 13 | self.high = high 14 | self.dist = self.high - self.low 15 | assert low.shape == high.shape 16 | self.shape = low.shape 17 | self.dtype = jnp.int16 18 | 19 | def sample(self, rng: chex.PRNGKey) -> chex.Array: 20 | return ( 21 | jax.random.uniform(rng, shape=self.shape, minval=0, maxval=1) * self.dist 22 | + self.low 23 | ).astype(self.dtype) 24 | 25 | def contains(self, x) -> jnp.ndarray: 26 | """Check whether specific object is within space.""" 27 | # type_cond = isinstance(x, self.dtype) 28 | # shape_cond = (x.shape == self.shape) 29 | range_cond = jnp.logical_and(x >= 0, x < self.n) 30 | return range_cond 31 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/luxai_s3/utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def to_numpy(x): 5 | if isinstance(x, dict): 6 | return {k: to_numpy(v) for k, v in x.items()} 7 | elif isinstance(x, list): 8 | return np.array(x) 9 | elif isinstance(x, np.ndarray): 10 | return x 11 | else: 12 | return np.array(x) 13 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/kit.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | def to_json(obj): 3 | if isinstance(obj, np.ndarray): 4 | return obj.tolist() 5 | elif isinstance(obj, np.integer): 6 | return int(obj) 7 | elif isinstance(obj, np.floating): 8 | return float(obj) 9 | elif isinstance(obj, list) or isinstance(obj, tuple): 10 | return [to_json(s) for s in obj] 11 | elif isinstance(obj, dict): 12 | out = {} 13 | for k in obj: 14 | out[k] = to_json(obj[k]) 15 | return out 16 | else: 17 | return obj 18 | def from_json(state): 19 | if isinstance(state, list): 20 | return np.array(state) 21 | elif isinstance(state, dict): 22 | out = {} 23 | for k in state: 24 | out[k] = from_json(state[k]) 25 | return out 26 | else: 27 | return state 28 | -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/utils.py: -------------------------------------------------------------------------------- 1 | # direction (0 = center, 1 = up, 2 = right, 3 = down, 4 = left) 2 | def direction_to(src, target): 3 | ds = target - src 4 | dx = ds[0] 5 | dy = ds[1] 6 | if dx == 0 and dy == 0: 7 | return 0 8 | if abs(dx) > abs(dy): 9 | if dx > 0: 10 | return 2 11 | else: 12 | return 4 13 | else: 14 | if dy > 0: 15 | return 3 16 | else: 17 | return 1 -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/test_agents/python/main.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | from argparse import Namespace 4 | if __package__ == "": 5 | from agent import Agent 6 | from lux.kit import from_json 7 | else: 8 | from .agent import Agent 9 | from .lux.kit import from_json 10 | ### DO NOT REMOVE THE FOLLOWING CODE ### 11 | agent_dict = dict() # store potentially multiple dictionaries as kaggle imports code directly 12 | agent_prev_obs = dict() 13 | def agent_fn(observation, configurations): 14 | """ 15 | agent definition for kaggle submission. 16 | """ 17 | global agent_dict 18 | obs = observation.obs 19 | if type(obs) == str: 20 | obs = json.loads(obs) 21 | step = observation.step 22 | player = observation.player 23 | remainingOverageTime = observation.remainingOverageTime 24 | if step == 0: 25 | agent_dict[player] = Agent(player, configurations["env_cfg"]) 26 | agent = agent_dict[player] 27 | actions = agent.act(step, from_json(obs), remainingOverageTime) 28 | return dict(action=actions.tolist()) 29 | if __name__ == "__main__": 30 | 31 | def read_input(): 32 | """ 33 | Reads input from stdin 34 | """ 35 | try: 36 | return input() 37 | except EOFError as eof: 38 | raise SystemExit(eof) 39 | step = 0 40 | player_id = 0 41 | env_cfg = None 42 | i = 0 43 | while True: 44 | inputs = read_input() 45 | raw_input = json.loads(inputs) 46 | observation = Namespace(**dict(step=raw_input["step"], obs=raw_input["obs"], remainingOverageTime=raw_input["remainingOverageTime"], player=raw_input["player"], info=raw_input["info"])) 47 | if i == 0: 48 | env_cfg = raw_input["info"]["env_cfg"] 49 | player_id = raw_input["player"] 50 | i += 1 51 | actions = agent_fn(observation, dict(env_cfg=env_cfg)) 52 | # send actions to engine 53 | print(json.dumps(actions)) -------------------------------------------------------------------------------- /kaggle_environments/envs/lux_ai_s3/test_lux.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from kaggle_environments import make 3 | 4 | def xtest_lux_completes(): 5 | env = make("lux_ai_s3", debug=True) 6 | env.run(["random_agent", "random_agent"]) 7 | json = env.toJSON() 8 | assert json["name"] == "lux_ai_s3" 9 | assert json["statuses"] == ["DONE", "DONE"] 10 | -------------------------------------------------------------------------------- /kaggle_environments/envs/mab/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/mab/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/mab/agents.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | def random_agent(observation, configuration): 5 | return random.randrange(configuration.banditCount - 1) 6 | 7 | 8 | def round_robin_agent(observation, configuration): 9 | return observation.step % configuration.banditCount 10 | 11 | 12 | agents = { 13 | "random": random_agent, 14 | "round_robin": round_robin_agent 15 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/mab/mab.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mab", 3 | "title": "Adversarial Multi-armed Bandit", 4 | "description": "Adversarial Multi-armed Bandit", 5 | "version": "1.0.0", 6 | "agents": [2, 3, 4, 5, 6, 7, 8], 7 | "configuration": { 8 | "episodeSteps": 2000, 9 | "actTimeout": 0.25, 10 | "banditCount": { 11 | "description": "Number of bandits available to choose from. Max action is this number -1.", 12 | "type": "integer", 13 | "minimum": 2, 14 | "default": 100 15 | }, 16 | "decayRate": { 17 | "description": "Rate that reward chance threshold increases per step that a bandit is chosen by an agent.", 18 | "type": "number", 19 | "minimum": 0, 20 | "default": 0.97 21 | }, 22 | "sampleResolution": { 23 | "description": "Maximum value that can be returned by a bandit.", 24 | "type": "integer", 25 | "minimum": 1, 26 | "default": 100 27 | } 28 | }, 29 | "reward": { 30 | "description": "The total reward each player has accumulated from the bandits across all steps.", 31 | "type": "number", 32 | "minimum": 0, 33 | "default": 0 34 | }, 35 | "observation": { 36 | "remainingOverageTime": 60, 37 | "agentIndex": { 38 | "description": "The current agent's index within observation.lastActions.", 39 | "type": "integer", 40 | "defaults": [0, 1, 2, 3, 4, 5, 6, 7] 41 | }, 42 | "reward": { 43 | "description": "Current reward of the agent.", 44 | "type": "number", 45 | "minimum": 0, 46 | "default": 0 47 | }, 48 | "lastActions": { 49 | "description": "Bandits chosen by each agent on the last step. None on the first step.", 50 | "type": "array", 51 | "shared": true, 52 | "items": { 53 | "type": "number", 54 | "minimum": 0 55 | }, 56 | "default": [] 57 | }, 58 | "thresholds": { 59 | "description": "Probability values for each machine payout on this step. Hidden from agents at runtime.", 60 | "type": "array", 61 | "shared": true, 62 | "hidden": true, 63 | "items": { 64 | "type": "number", 65 | "minimum": 0 66 | } 67 | } 68 | }, 69 | "action": { 70 | "description": "Choice of bandit for the step (min bandit = 0, max bandit = configuration.banditCount - 1)", 71 | "type": "integer", 72 | "minimum": 0 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /kaggle_environments/envs/open_spiel/test_open_spiel.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from kaggle_environments import make 3 | import open_spiel as open_spiel_env 4 | 5 | 6 | def test_envs_load(): 7 | envs = open_spiel_env._register_open_spiel_envs() 8 | print(len(envs)) 9 | 10 | 11 | def test_tic_tac_toe_playthrough(): 12 | envs = open_spiel_env._register_open_spiel_envs(["tic_tac_toe"]) 13 | print(envs) 14 | env = make("open_spiel_tic_tac_toe", debug=True) 15 | env.run(["random", "random"]) 16 | json = env.toJSON() 17 | assert json["name"] == "open_spiel_tic_tac_toe" 18 | assert all([status == "DONE" for status in json["statuses"]]) -------------------------------------------------------------------------------- /kaggle_environments/envs/rps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaggle/kaggle-environments/51d191900612b7cf39fdaff206971d47db0321ae/kaggle_environments/envs/rps/__init__.py -------------------------------------------------------------------------------- /kaggle_environments/envs/rps/agents.py: -------------------------------------------------------------------------------- 1 | import random 2 | from .utils import get_score 3 | 4 | 5 | def rock(observation, configuration): 6 | return 0 7 | 8 | 9 | def paper(observation, configuration): 10 | return 1 11 | 12 | 13 | def scissors(observation, configuration): 14 | return 2 15 | 16 | 17 | def copy_opponent(observation, configuration): 18 | if observation.step > 0: 19 | return observation.lastOpponentAction 20 | else: 21 | return random.randrange(0, configuration.signs) 22 | 23 | 24 | last_react_action = None 25 | 26 | 27 | def reactionary(observation, configuration): 28 | global last_react_action 29 | if observation.step == 0: 30 | last_react_action = random.randrange(0, configuration.signs) 31 | elif get_score(last_react_action, observation.lastOpponentAction) <= 1: 32 | last_react_action = (observation.lastOpponentAction + 1) % configuration.signs 33 | 34 | return last_react_action 35 | 36 | 37 | last_counter_action = None 38 | 39 | 40 | def counter_reactionary(observation, configuration): 41 | global last_counter_action 42 | if observation.step == 0: 43 | last_counter_action = random.randrange(0, configuration.signs) 44 | elif get_score(last_counter_action, observation.lastOpponentAction) == 1: 45 | last_counter_action = (last_counter_action + 2) % configuration.signs 46 | else: 47 | last_counter_action = (observation.lastOpponentAction + 1) % configuration.signs 48 | 49 | return last_counter_action 50 | 51 | 52 | action_histogram = {} 53 | 54 | 55 | def statistical(observation, configuration): 56 | global action_histogram 57 | if observation.step == 0: 58 | action_histogram = {} 59 | return 60 | action = observation.lastOpponentAction 61 | if action not in action_histogram: 62 | action_histogram[action] = 0 63 | action_histogram[action] += 1 64 | mode_action = None 65 | mode_action_count = None 66 | for k, v in action_histogram.items(): 67 | if mode_action_count is None or v > mode_action_count: 68 | mode_action = k 69 | mode_action_count = v 70 | continue 71 | 72 | return (mode_action + 1) % configuration.signs 73 | 74 | 75 | agents = { 76 | "rock": rock, 77 | "paper": paper, 78 | "scissors": scissors, 79 | "copy_opponent": copy_opponent, 80 | "reactionary": reactionary, 81 | "counter_reactionary": counter_reactionary, 82 | "statistical": statistical 83 | } -------------------------------------------------------------------------------- /kaggle_environments/envs/rps/helpers.py: -------------------------------------------------------------------------------- 1 | import json 2 | from os import path 3 | from kaggle_environments.helpers import * 4 | 5 | 6 | class Observation(Observation): 7 | """ 8 | Observation primarily used as a helper to construct the State from the raw observation. 9 | This provides bindings for the observation type described at https://github.com/Kaggle/kaggle-environments/blob/master/kaggle_environments/envs/rps/rps.json 10 | """ 11 | @property 12 | def last_opponent_action(self) -> int: 13 | """Move the opponent took on the last turn.""" 14 | return self["halite"] 15 | 16 | 17 | class Configuration(Configuration): 18 | """ 19 | Configuration provides access to tunable parameters in the environment. 20 | This provides bindings for the configuration type described at https://github.com/Kaggle/kaggle-environments/blob/master/kaggle_environments/envs/rps/rps.json 21 | """ 22 | @property 23 | def signs(self) -> int: 24 | """Number of choices each step (3 for the normal rock, paper, scissors)""" 25 | return self["signs"] 26 | -------------------------------------------------------------------------------- /kaggle_environments/envs/rps/rps.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rps", 3 | "title": "Rock Paper Scissors", 4 | "description": "Repeated Rock Paper Scissors", 5 | "version": "1.0.0", 6 | "agents": [2], 7 | "configuration": { 8 | "signs": { 9 | "description": "Number of choices each step (Rock = 0, Paper = 1, Scissors = 2, etc).", 10 | "type": "integer", 11 | "minimum": 3, 12 | "maximum": 5, 13 | "default": 3 14 | }, 15 | "episodeSteps": { 16 | "description": "Maximum number of steps the environment can run. Total is this number -1.", 17 | "type": "integer", 18 | "minimum": 2, 19 | "default": 1000 20 | }, 21 | "tieRewardThreshold": { 22 | "description": "Minimum reward needed to achieve a win rather than a tie.", 23 | "type": "integer", 24 | "minimum": 1, 25 | "maximum": 1000, 26 | "default": 20 27 | }, 28 | "agentTimeout": { 29 | "description": "Obsolete field kept for backwards compatibility, please use observation.remainingOverageTime.", 30 | "type": "number", 31 | "minimum": 0, 32 | "default": 60 33 | }, 34 | "actTimeout": 1 35 | }, 36 | "reward": { 37 | "description": "-1 = Lost, 0 = Draw, 1 = Won", 38 | "enum": [-1, 0, 1], 39 | "default": 0 40 | }, 41 | "observation": { 42 | "lastOpponentAction": { 43 | "description": "Symbol returned by opponent last step. None on the first step.", 44 | "type": "integer", 45 | "minimum": 0, 46 | "maximum": 4 47 | }, 48 | "remainingOverageTime": 60, 49 | "reward": { 50 | "description": "Current reward of the agent.", 51 | "type": "integer", 52 | "minimum": -999, 53 | "maximum": 999, 54 | "default": 0 55 | } 56 | }, 57 | "action": { 58 | "description": "Choice of sign for the step (Rock = 0, Paper = 1, Scissors = 2, etc)", 59 | "type": "integer", 60 | "minimum": 0, 61 | "maximum": 4 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /kaggle_environments/envs/rps/rps.py: -------------------------------------------------------------------------------- 1 | import json 2 | from os import path 3 | from .agents import agents as all_agents 4 | from .utils import get_score 5 | 6 | 7 | def interpreter(state, env): 8 | player1 = state[0] 9 | player2 = state[1] 10 | 11 | # Specification can fully handle the reset. 12 | if env.done: 13 | return state 14 | 15 | def is_valid_action(player, sign_count): 16 | return ( 17 | player.action is not None and 18 | isinstance(player.action, int) and 19 | 0 <= player.action < sign_count 20 | ) 21 | 22 | # Check for validity of actions 23 | is_player1_valid = is_valid_action(player1, env.configuration.signs) 24 | is_player2_valid = is_valid_action(player2, env.configuration.signs) 25 | if not is_player2_valid: 26 | player2.status = "INVALID" 27 | player2.reward = 0 28 | 29 | if is_player1_valid: 30 | player1.status = "DONE" 31 | player1.reward = 1 32 | return state 33 | 34 | if not is_player1_valid: 35 | player1.status = "INVALID" 36 | player1.reward = 0 37 | 38 | if is_player2_valid: 39 | player2.status = "DONE" 40 | player2.reward = 1 41 | return state 42 | else: 43 | return state 44 | 45 | score = get_score(player1.action, player2.action) 46 | player1.observation.lastOpponentAction = player2.action 47 | player1.reward += score 48 | player2.observation.lastOpponentAction = player1.action 49 | player2.reward -= score 50 | player1.observation.reward = int(player1.reward) 51 | player2.observation.reward = int(player2.reward) 52 | remaining_steps = env.configuration.episodeSteps - player1.observation.step - 1 53 | 54 | # This is the last step 55 | if remaining_steps <= 1: 56 | player1.status = "DONE" 57 | player2.status = "DONE" 58 | # Player performance too similar, consider the match a tie. 59 | if abs(player1.reward) < env.configuration.tieRewardThreshold: 60 | player1.reward = 0 61 | player2.reward = 0 62 | return state 63 | 64 | 65 | def renderer(state, env): 66 | sign_names = ["Rock", "Paper", "Scissors", "Spock", "Lizard"] 67 | rounds_played = len(env.steps) 68 | board = "" 69 | 70 | # This line prints results each round, good for debugging 71 | for i in range(1, rounds_played): 72 | step = env.steps[i] 73 | right_move = step[0].observation.lastOpponentAction 74 | left_move = step[1].observation.lastOpponentAction 75 | board += f"Round {i}: {sign_names[left_move]} vs {sign_names[right_move]}, Score: {step[0].reward} to {step[1].reward}\n" 76 | 77 | board += f"Game ended on round {rounds_played - 1}, final score: {state[0].reward} to {state[0].reward}\n" 78 | return board 79 | 80 | 81 | dir_path = path.dirname(__file__) 82 | json_path = path.abspath(path.join(dir_path, "rps.json")) 83 | with open(json_path) as json_file: 84 | specification = json.load(json_file) 85 | 86 | 87 | def html_renderer(): 88 | js_path = path.abspath(path.join(dir_path, "rps.js")) 89 | with open(js_path, encoding="utf-8") as js_file: 90 | return js_file.read() 91 | 92 | 93 | agents = all_agents 94 | -------------------------------------------------------------------------------- /kaggle_environments/envs/rps/utils.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | 4 | def get_score(left_move, right_move): 5 | # This method exists in this file so it can be consumed from rps.py and agents.py without a circular dependency 6 | delta = ( 7 | right_move - left_move 8 | if (left_move + right_move) % 2 == 0 9 | else left_move - right_move 10 | ) 11 | return 0 if delta == 0 else math.copysign(1, delta) -------------------------------------------------------------------------------- /kaggle_environments/envs/tictactoe/tictactoe.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tictactoe", 3 | "title": "Tic Tac Toe", 4 | "description": "Classic Tic Tac Toe", 5 | "version": "1.0.0", 6 | "agents": [2], 7 | "configuration": { 8 | "episodeSteps": 10, 9 | "actTimeout": 1, 10 | "agentTimeout": { 11 | "description": "Obsolete field kept for backwards compatibility, please use observation.remainingOverageTime.", 12 | "type": "number", 13 | "minimum": 0, 14 | "default": 2 15 | } 16 | }, 17 | "reward": { 18 | "description": "-1 = Lost, 0 = Draw/Ongoing, 1 = Won", 19 | "enum": [-1, 0, 1], 20 | "default": 0 21 | }, 22 | "observation": { 23 | "board": { 24 | "description": "Serialized 3x3 grid. 0 = Empty, 1 = X, 2 = O", 25 | "type": "array", 26 | "shared": true, 27 | "default": [0, 0, 0, 0, 0, 0, 0, 0, 0], 28 | "minItems": 9, 29 | "maxItems": 9 30 | }, 31 | "mark": { 32 | "description": "Mark for the agent to use", 33 | "defaults": [1, 2], 34 | "enum": [1, 2] 35 | }, 36 | "remainingOverageTime": 2 37 | }, 38 | "action": { 39 | "description": "Position to place a mark on the board.", 40 | "type": "integer", 41 | "minimum": 0, 42 | "maximum": 8 43 | }, 44 | "status": { 45 | "defaults": ["ACTIVE", "INACTIVE"] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /kaggle_environments/status_codes.json: -------------------------------------------------------------------------------- 1 | { 2 | "OK": { 3 | "code": 0, 4 | "status": 200 5 | }, 6 | "CANCELLED": { 7 | "code": 1, 8 | "status": 499 9 | }, 10 | "UNKNOWN": { 11 | "code": 2, 12 | "status": 500 13 | }, 14 | "INVALID_ARGUMENT": { 15 | "code": 3, 16 | "status": 400 17 | }, 18 | "DEADLINE_EXCEEDED": { 19 | "code": 4, 20 | "status": 504 21 | }, 22 | "NOT_FOUND": { 23 | "code": 5, 24 | "status": 404 25 | }, 26 | "ALREADY_EXISTS": { 27 | "code": 6, 28 | "status": 409 29 | }, 30 | "PERMISSION_DENIED": { 31 | "code": 7, 32 | "status": 403 33 | }, 34 | "UNAUTHENTICATED": { 35 | "code": 16, 36 | "status": 401 37 | }, 38 | "RESOURCE_EXHAUSTED": { 39 | "code": 8, 40 | "status": 429 41 | }, 42 | "FAILED_PRECONDITION": { 43 | "code": 9, 44 | "status": 400 45 | }, 46 | "ABORTED": { 47 | "code": 10, 48 | "status": 409 49 | }, 50 | "OUT_OF_RANGE": { 51 | "code": 11, 52 | "status": 400 53 | }, 54 | "UNIMPLEMENTED": { 55 | "code": 12, 56 | "status": 501 57 | }, 58 | "INTERNAL": { 59 | "code": 13, 60 | "status": 500 61 | }, 62 | "UNAVAILABLE": { 63 | "code": 14, 64 | "status": 503 65 | }, 66 | "DATA_LOSS": { 67 | "code": 15, 68 | "status": 500 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | rm -rf ./dist 2 | rm -rf ./build 3 | rm -rf ./kaggle_environments.egg-info 4 | # Delete pycache, pyc, and pyo files 5 | find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf 6 | python3 setup.py sdist 7 | python3 setup.py bdist_wheel --universal -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | twine upload dist/* -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | gym 3 | ipython 4 | jsonschema 5 | numpy 6 | requests -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright 2020 Kaggle Inc 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # coding=utf-8 17 | from setuptools import setup, find_packages 18 | import codecs 19 | import os.path 20 | 21 | def read(rel_path): 22 | here = os.path.abspath(os.path.dirname(__file__)) 23 | with codecs.open(os.path.join(here, rel_path), 'r') as fp: 24 | return fp.read() 25 | 26 | def get_version(rel_path): 27 | for line in read(rel_path).splitlines(): 28 | if line.startswith('__version__'): 29 | delim = '"' if '"' in line else "'" 30 | return line.split(delim)[1] 31 | else: 32 | raise RuntimeError("Unable to find version string.") 33 | 34 | with open("README.md", "r") as f: 35 | long_description = f.read() 36 | 37 | setup( 38 | name='kaggle-environments', 39 | version=get_version("kaggle_environments/__init__.py"), 40 | description='Kaggle Environments', 41 | long_description=long_description, 42 | long_description_content_type='text/markdown', 43 | author='Kaggle', 44 | author_email='support@kaggle.com', 45 | url='https://github.com/Kaggle/kaggle-environments', 46 | keywords=['Kaggle'], 47 | entry_points={'console_scripts': [ 48 | 'kaggle-environments = kaggle_environments.main:main']}, 49 | install_requires=[ 50 | "jsonschema >= 3.0.1", 51 | "Flask >= 1.1.2", 52 | "numpy >= 1.19.5", 53 | "requests >= 2.25.1", 54 | "pettingzoo == 1.24.0", 55 | "gymnasium == 0.29.0", 56 | "stable-baselines3 == 2.1.0", 57 | "transformers >= 4.33.1", 58 | "scipy >= 1.11.2", 59 | "shimmy >= 1.2.1", 60 | "Chessnut >= 0.4.1", 61 | "open_spiel >= 1.5.0", 62 | ], 63 | packages=find_packages(), 64 | include_package_data=True, 65 | python_requires='>=3.8', 66 | license='Apache 2.0') 67 | --------------------------------------------------------------------------------