├── .coveragerc
├── .github
└── workflows
│ └── unit_test.yml
├── .gitignore
├── LICENSE
├── README.md
├── assets
├── banner.png
├── eat_others.gif
├── full.gif
├── merge.gif
├── mid_eject.gif
├── overview.gif
├── partial.gif
├── qr.png
└── teamwork.gif
├── docs
├── en
│ ├── Makefile
│ ├── make.bat
│ ├── requirements.txt
│ └── source
│ │ ├── _static
│ │ └── css
│ │ │ └── style.css
│ │ ├── _templates
│ │ └── layout.html
│ │ ├── advanced
│ │ ├── cfg_intro.rst
│ │ ├── collision.rst
│ │ └── images
│ │ │ ├── 2f2s.png
│ │ │ ├── 2f2s_v2.png
│ │ │ ├── 2f2s_v3.png
│ │ │ ├── changed_num_3000.png
│ │ │ ├── eighth_merge.gif
│ │ │ ├── iters_num_3000.png
│ │ │ ├── quarter_merge.gif
│ │ │ ├── query_num_3000.png
│ │ │ └── straight_merge.gif
│ │ ├── api_doc
│ │ ├── agents.rst
│ │ ├── balls.rst
│ │ ├── index.rst
│ │ ├── managers.rst
│ │ ├── players.rst
│ │ ├── render.rst
│ │ ├── server.rst
│ │ └── utils.rst
│ │ ├── community
│ │ └── faq.rst
│ │ ├── conf.py
│ │ ├── index.rst
│ │ ├── installation
│ │ └── index.rst
│ │ └── tutorial
│ │ ├── gobigger_engine.rst
│ │ ├── gobigger_env.rst
│ │ ├── images
│ │ ├── eat_food.gif
│ │ ├── eat_player.gif
│ │ ├── eject.gif
│ │ ├── eject_and_move.gif
│ │ ├── eject_cross.gif
│ │ ├── eject_merger.gif
│ │ ├── eject_to_thorns.gif
│ │ ├── fast_eat.gif
│ │ ├── merge_quickly.gif
│ │ ├── on_thorns.gif
│ │ ├── overview.gif
│ │ ├── split.gif
│ │ ├── split_eat_all.gif
│ │ └── split_merge.gif
│ │ ├── index.rst
│ │ ├── playback.rst
│ │ ├── quick_start.rst
│ │ ├── real_time_interaction_with_game.rst
│ │ ├── space.rst
│ │ └── what_is_gobigger.rst
└── zh_CN
│ ├── Makefile
│ ├── make.bat
│ ├── requirements.txt
│ └── source
│ ├── _static
│ └── css
│ │ └── style.css
│ ├── _templates
│ └── layout.html
│ ├── advanced
│ ├── cfg_intro.rst
│ ├── collision.rst
│ └── images
│ │ ├── 2f2s.png
│ │ ├── 2f2s_v2.png
│ │ ├── 2f2s_v3.png
│ │ ├── changed_num_3000.png
│ │ ├── eighth_merge.gif
│ │ ├── iters_num_3000.png
│ │ ├── quarter_merge.gif
│ │ ├── query_num_3000.png
│ │ └── straight_merge.gif
│ ├── community
│ └── faq.rst
│ ├── conf.py
│ ├── index.rst
│ ├── installation
│ └── index.rst
│ └── tutorial
│ ├── gobigger_engine.rst
│ ├── gobigger_env.rst
│ ├── images
│ ├── eat_food.gif
│ ├── eat_player.gif
│ ├── eject.gif
│ ├── eject_and_move.gif
│ ├── eject_cross.gif
│ ├── eject_merger.gif
│ ├── eject_to_thorns.gif
│ ├── fast_eat.gif
│ ├── merge_quickly.gif
│ ├── on_thorns.gif
│ ├── overview.gif
│ ├── split.gif
│ ├── split_eat_all.gif
│ └── split_merge.gif
│ ├── index.rst
│ ├── playback.rst
│ ├── quick_start.rst
│ ├── real_time_interaction_with_game.rst
│ ├── space.rst
│ └── what_is_gobigger.rst
├── gobigger
├── __init__.py
├── agents
│ ├── __init__.py
│ ├── base_agent.py
│ ├── bot_agent.py
│ └── tests
│ │ ├── test_base_agent.py
│ │ └── test_bot_agent.py
├── balls
│ ├── __init__.py
│ ├── base_ball.py
│ ├── clone_ball.py
│ ├── food_ball.py
│ ├── spore_ball.py
│ ├── tests
│ │ ├── test_base_ball.py
│ │ ├── test_clone_ball.py
│ │ ├── test_food_ball.py
│ │ ├── test_spore_ball.py
│ │ └── test_thorns_ball.py
│ └── thorns_ball.py
├── bin
│ ├── __init__.py
│ ├── demo_bot.py
│ ├── play.py
│ ├── play_hyper.py
│ ├── profile.py
│ └── replayer.py
├── configs
│ ├── __init__.py
│ ├── server_default_config.py
│ ├── server_sp_default_config.py
│ ├── sp_t4p3.py
│ ├── st_t1p1.py
│ ├── st_t1p2.py
│ ├── st_t2p1.py
│ ├── st_t2p2.py
│ ├── st_t3p2.py
│ ├── st_t4p3.py
│ ├── st_t5p3.py
│ ├── st_t5p4.py
│ └── st_t6p4.py
├── envs
│ ├── __init__.py
│ ├── gobigger_env.py
│ ├── gobigger_sp_env.py
│ └── tests
│ │ ├── test_gobigger_env.py
│ │ └── test_gobigger_sp_env.py
├── managers
│ ├── __init__.py
│ ├── base_manager.py
│ ├── food_manager.py
│ ├── player_manager.py
│ ├── player_sp_manager.py
│ ├── spore_manager.py
│ ├── tests
│ │ ├── test_base_manager.py
│ │ ├── test_food_manager.py
│ │ ├── test_player_manager.py
│ │ ├── test_player_sp_manager.py
│ │ ├── test_spore_manager.py
│ │ └── test_thorns_manager.py
│ └── thorns_manager.py
├── playbacks
│ ├── __init__.py
│ ├── action_pb.py
│ ├── base_pb.py
│ ├── frame_pb.py
│ ├── null_pb.py
│ ├── tests
│ │ └── test_playback.py
│ └── video_pb.py
├── players
│ ├── __init__.py
│ ├── base_player.py
│ ├── human_player.py
│ ├── human_sp_player.py
│ └── tests
│ │ ├── test_base_player.py
│ │ ├── test_human_player.py
│ │ └── test_human_sp_player.py
├── render
│ ├── __init__.py
│ ├── base_render.py
│ ├── env_render.py
│ ├── pb_render.py
│ ├── realtime_render.py
│ └── tests
│ │ ├── test_env_render.py
│ │ └── test_realtime_render.py
├── server
│ ├── __init__.py
│ ├── server.py
│ ├── server_sp.py
│ └── tests
│ │ ├── test_server.py
│ │ └── test_server_sp.py
└── utils
│ ├── __init__.py
│ ├── collision_detection.py
│ ├── colors.py
│ ├── config_utils.py
│ ├── frame_utils.py
│ ├── generator_utils.py
│ ├── obs_utils.py
│ ├── structures.py
│ ├── test
│ ├── test_collision_detection.py
│ ├── test_config_utils.py
│ ├── test_sequence_generator.py
│ └── test_structures.py
│ └── tool.py
├── practice
├── README.md
├── battle.py
├── cooperative_agent
│ ├── agent.py
│ ├── default_model_config.yaml
│ └── model.py
├── solo_agent
│ ├── agent.py
│ ├── default_model_config.yaml
│ ├── model.py
│ └── util.py
├── test_ai.py
└── tools
│ ├── encoder.py
│ ├── features.py
│ ├── head.py
│ ├── network
│ ├── __init__.py
│ ├── activation.py
│ ├── encoder.py
│ ├── nn_module.py
│ ├── normalization.py
│ ├── res_block.py
│ ├── rnn.py
│ ├── scatter_connection.py
│ ├── soft_argmax.py
│ └── transformer.py
│ └── util.py
└── setup.py
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | omit = gobigger/bin/*
--------------------------------------------------------------------------------
/.github/workflows/unit_test.yml:
--------------------------------------------------------------------------------
1 | # This workflow will check pytest
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3 |
4 | name: unit_test
5 |
6 | on: [push, pull_request]
7 |
8 | jobs:
9 | test_unittest:
10 | runs-on: ubuntu-latest
11 | if: "!contains(github.event.head_commit.message, 'ci skip')"
12 | strategy:
13 | matrix:
14 | python-version: [3.7]
15 | steps:
16 | - uses: actions/checkout@v2
17 | with:
18 | fetch-depth: 2
19 | - name: Set up Python ${{ matrix.python-version }}
20 | uses: actions/setup-python@v2
21 | with:
22 | python-version: ${{ matrix.python-version }}
23 | - name: Install depedendencies
24 | run: |
25 | pip install pytest-cov
26 | pip install -e .
27 | - name: Generate coverage report
28 | run: |
29 | pytest ./gobigger --cov=./ --cov-report=xml --cov-report=term-missing --durations=0 -v
30 | - name: Upload coverage to Codecov
31 | uses: codecov/codecov-action@v1
32 | with:
33 | token: ${{ secrets.CODECOV_TOKEN }}
34 | file: ./coverage.xml
35 | flags: unittests
36 | name: codecov-umbrella
37 | fail_ci_if_error: false
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 | *.mp4
131 | .idea/
132 |
133 | *.DS_Store
134 | *.pb
135 | *.ac
136 |
--------------------------------------------------------------------------------
/assets/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/assets/banner.png
--------------------------------------------------------------------------------
/assets/eat_others.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/assets/eat_others.gif
--------------------------------------------------------------------------------
/assets/full.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/assets/full.gif
--------------------------------------------------------------------------------
/assets/merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/assets/merge.gif
--------------------------------------------------------------------------------
/assets/mid_eject.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/assets/mid_eject.gif
--------------------------------------------------------------------------------
/assets/overview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/assets/overview.gif
--------------------------------------------------------------------------------
/assets/partial.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/assets/partial.gif
--------------------------------------------------------------------------------
/assets/qr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/assets/qr.png
--------------------------------------------------------------------------------
/assets/teamwork.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/assets/teamwork.gif
--------------------------------------------------------------------------------
/docs/en/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/en/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/en/requirements.txt:
--------------------------------------------------------------------------------
1 | easydict
2 | gym
3 | pygame
4 | pytest
5 | opencv-python
6 | numpy
7 | sphinx
8 | tqdm
9 | trueskill
10 | git+http://github.com/opendilab/GoBigger@main
11 |
--------------------------------------------------------------------------------
/docs/en/source/_static/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;
3 | }
4 |
5 | /* Default header fonts are ugly */
6 | h1, h2, .rst-content .toctree-wrapper p.caption, h3, h4, h5, h6, legend, p.caption {
7 | font-family: "Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;
8 | }
9 |
10 | /* Use white for docs background */
11 | .wy-side-nav-search {
12 | background-color: #fff;
13 | }
14 |
15 | .wy-nav-content {
16 | max-width: 1200px !important;
17 | }
18 |
19 | .wy-nav-content-wrap, .wy-menu li.current > a {
20 | background-color: #fff;
21 | }
22 |
23 | .wy-side-nav-search>a img.logo {
24 | width: 80%;
25 | margin-top: 10px;
26 | }
27 |
28 | @media screen and (min-width: 1400px) {
29 | .wy-nav-content-wrap {
30 | background-color: #fff;
31 | }
32 |
33 | .wy-nav-content {
34 | background-color: #fff;
35 | }
36 | }
37 |
38 | /* Fixes for mobile */
39 | .wy-nav-top {
40 | background-color: #fff;
41 | /*background-image: url('../images/tianshou-logo.png');*/
42 | background-repeat: no-repeat;
43 | background-position: center;
44 | padding: 0;
45 | margin: 0.4045em 0.809em;
46 | color: #333;
47 | }
48 |
49 | .wy-nav-top > a {
50 | display: none;
51 | }
52 |
53 | @media screen and (max-width: 768px) {
54 | .wy-side-nav-search>a img.logo {
55 | height: 60px;
56 | }
57 | }
58 |
59 | /* This is needed to ensure that logo above search scales properly */
60 | .wy-side-nav-search a {
61 | display: block;
62 | }
63 |
64 | /* This ensures that multiple constructors will remain in separate lines. */
65 | .rst-content dl:not(.docutils) dt {
66 | display: table;
67 | }
68 |
69 | /* Use our red for literals (it's very similar to the original color) */
70 | .rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal {
71 | color: #4692BC;
72 | }
73 |
74 | .rst-content tt.xref, a .rst-content tt, .rst-content tt.xref,
75 | .rst-content code.xref, a .rst-content tt, a .rst-content code {
76 | color: #404040;
77 | }
78 |
79 | /* Change link colors (except for the menu) */
80 |
81 | a {
82 | color: #4692BC;
83 | }
84 |
85 | a:hover {
86 | color: #4692BC;
87 | }
88 |
89 |
90 | a:visited {
91 | color: #4692BC;
92 | }
93 |
94 | .wy-menu a {
95 | color: #b3b3b3;
96 | }
97 |
98 | .wy-menu a:hover {
99 | color: #b3b3b3;
100 | }
101 |
102 | /* Default footer text is quite big */
103 | footer {
104 | font-size: 80%;
105 | }
106 |
107 | footer .rst-footer-buttons {
108 | font-size: 125%; /* revert footer settings - 1/80% = 125% */
109 | }
110 |
111 | footer p {
112 | font-size: 100%;
113 | }
114 |
115 | .ethical-rtd {
116 | display: none;
117 | }
118 |
119 | .ethical-fixedfooter {
120 | display: none;
121 | }
122 |
123 | .ethical-content {
124 | display: none;
125 | }
126 |
127 | /* For hidden headers that appear in TOC tree */
128 | /* see http://stackoverflow.com/a/32363545/3343043 */
129 | .rst-content .hidden-section {
130 | display: none;
131 | }
132 |
133 | nav .hidden-section {
134 | display: inherit;
135 | }
136 |
137 | .wy-side-nav-search>div.version {
138 | color: #000;
139 | }
--------------------------------------------------------------------------------
/docs/en/source/_templates/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "!layout.html" %}
2 | {% block extrahead %}
3 |
4 | {% endblock %}
--------------------------------------------------------------------------------
/docs/en/source/advanced/cfg_intro.rst:
--------------------------------------------------------------------------------
1 | Configuration
2 | ##########################################
3 |
4 |
5 | Overview
6 | ======================
7 |
8 | In order to allow players to further understand the realization of the game mechanism, we have opened some parameters for players to adjust. We hope that players can modify the corresponding parameters to achieve a variety of different environments. At the same time, you can also design your own agent environment to quickly verify the algorithm.
9 |
10 | GoBigger puts configurable parameters in ``gobigger/server/server_default_config.py``
11 |
--------------------------------------------------------------------------------
/docs/en/source/advanced/images/2f2s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/advanced/images/2f2s.png
--------------------------------------------------------------------------------
/docs/en/source/advanced/images/2f2s_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/advanced/images/2f2s_v2.png
--------------------------------------------------------------------------------
/docs/en/source/advanced/images/2f2s_v3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/advanced/images/2f2s_v3.png
--------------------------------------------------------------------------------
/docs/en/source/advanced/images/changed_num_3000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/advanced/images/changed_num_3000.png
--------------------------------------------------------------------------------
/docs/en/source/advanced/images/eighth_merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/advanced/images/eighth_merge.gif
--------------------------------------------------------------------------------
/docs/en/source/advanced/images/iters_num_3000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/advanced/images/iters_num_3000.png
--------------------------------------------------------------------------------
/docs/en/source/advanced/images/quarter_merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/advanced/images/quarter_merge.gif
--------------------------------------------------------------------------------
/docs/en/source/advanced/images/query_num_3000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/advanced/images/query_num_3000.png
--------------------------------------------------------------------------------
/docs/en/source/advanced/images/straight_merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/advanced/images/straight_merge.gif
--------------------------------------------------------------------------------
/docs/en/source/api_doc/agents.rst:
--------------------------------------------------------------------------------
1 | agents
2 | ##################
3 |
4 | .. toctree::
5 | :maxdepth: 3
6 |
7 |
8 | BaseAgent
9 | =======================
10 | .. autoclass:: gobigger.agents.base_agent.BaseAgent
11 | :members:
12 |
13 |
14 | BotAgent
15 | =======================
16 | .. autoclass:: gobigger.agents.bot_agent.BotAgent
17 | :members:
18 |
--------------------------------------------------------------------------------
/docs/en/source/api_doc/balls.rst:
--------------------------------------------------------------------------------
1 | balls
2 | ##################
3 |
4 | .. toctree::
5 | :maxdepth: 3
6 |
7 |
8 | BaseBall
9 | =======================
10 | .. autoclass:: gobigger.balls.base_ball.BaseBall
11 | :members:
12 |
13 |
14 | FoodBall
15 | =======================
16 | .. autoclass:: gobigger.balls.food_ball.FoodBall
17 | :members:
18 |
19 |
20 | ThornsBall
21 | =======================
22 | .. autoclass:: gobigger.balls.thorns_ball.ThornsBall
23 | :members:
24 |
25 |
26 | CloneBall
27 | =======================
28 | .. autoclass:: gobigger.balls.clone_ball.CloneBall
29 | :members:
30 |
31 |
32 | SporeBall
33 | =======================
34 | .. autoclass:: gobigger.balls.spore_ball.SporeBall
35 | :members:
36 |
37 |
--------------------------------------------------------------------------------
/docs/en/source/api_doc/index.rst:
--------------------------------------------------------------------------------
1 | .. pod documentation master file, created by
2 | sphinx-quickstart on Jan 25 2021.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | API Doc
7 | ##########
8 |
9 | .. toctree::
10 | :maxdepth: 3
11 |
12 | agents
13 | balls
14 | managers
15 | players
16 | render
17 | server
18 | utils
19 |
--------------------------------------------------------------------------------
/docs/en/source/api_doc/managers.rst:
--------------------------------------------------------------------------------
1 | managers
2 | ##################
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 |
8 | BaseManager
9 | =======================
10 | .. autoclass:: gobigger.managers.base_manager.BaseManager
11 | :members:
12 |
13 |
14 | FoodManager
15 | =======================
16 | .. autoclass:: gobigger.managers.food_manager.FoodManager
17 | :members:
18 |
19 |
20 | ThornsManager
21 | =======================
22 | .. autoclass:: gobigger.managers.thorns_manager.ThornsManager
23 | :members:
24 |
25 |
26 | PlayerManager
27 | =======================
28 | .. autoclass:: gobigger.managers.player_manager.PlayerManager
29 | :members:
30 |
31 |
32 | SporeManager
33 | =======================
34 | .. autoclass:: gobigger.managers.spore_manager.SporeManager
35 | :members:
36 |
37 |
38 |
--------------------------------------------------------------------------------
/docs/en/source/api_doc/players.rst:
--------------------------------------------------------------------------------
1 | players
2 | ##################
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 |
8 | BasePlayer
9 | =======================
10 | .. autoclass:: gobigger.players.base_player.BasePlayer
11 | :members:
12 |
13 |
14 | HumanPlayer
15 | =======================
16 | .. autoclass:: gobigger.players.human_player.HumanPlayer
17 | :members:
18 |
--------------------------------------------------------------------------------
/docs/en/source/api_doc/render.rst:
--------------------------------------------------------------------------------
1 | render
2 | ##################
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 |
8 | BaseRender
9 | =======================
10 | .. autoclass:: gobigger.render.base_render.BaseRender
11 | :members:
12 |
13 |
14 | EnvRender
15 | =======================
16 | .. autoclass:: gobigger.render.env_render.EnvRender
17 | :members:
18 |
19 |
20 | RealtimeRender
21 | =======================
22 | .. autoclass:: gobigger.render.realtime_render.RealtimeRender
23 | :members:
24 |
25 |
26 | RealtimePartialRender
27 | =======================
28 | .. autoclass:: gobigger.render.realtime_render.RealtimePartialRender
29 | :members:
30 |
--------------------------------------------------------------------------------
/docs/en/source/api_doc/server.rst:
--------------------------------------------------------------------------------
1 | server
2 | ##################
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 |
8 | Server
9 | =======================
10 | .. autoclass:: gobigger.server.server.Server
11 | :members:
12 |
13 |
14 |
--------------------------------------------------------------------------------
/docs/en/source/api_doc/utils.rst:
--------------------------------------------------------------------------------
1 | utils
2 | ##################
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 |
8 | Border
9 | =======================
10 | .. autoclass:: gobigger.utils.structures.Border
11 | :members:
12 |
13 |
14 | BaseCollisionDetection
15 | =========================================
16 | .. autoclass:: gobigger.utils.collision_detection.BaseCollisionDetection
17 | :members:
18 |
19 | ExhaustiveCollisionDetection
20 | =========================================
21 | .. autoclass:: gobigger.utils.collision_detection.ExhaustiveCollisionDetection
22 | :members:
23 |
24 | PrecisionCollisionDetection
25 | =========================================
26 | .. autoclass:: gobigger.utils.collision_detection.PrecisionCollisionDetection
27 | :members:
28 |
29 | RebuildQuadTreeCollisionDetection
30 | =========================================
31 | .. autoclass:: gobigger.utils.collision_detection.RebuildQuadTreeCollisionDetection
32 | :members:
33 |
34 | RemoveQuadTreeCollisionDetection
35 | =========================================
36 | .. autoclass:: gobigger.utils.collision_detection.RemoveQuadTreeCollisionDetection
37 | :members:
--------------------------------------------------------------------------------
/docs/en/source/community/faq.rst:
--------------------------------------------------------------------------------
1 | FAQ
2 | ##############
3 |
4 |
5 | Q1: How to save video in a game?
6 | ***********************************
7 |
8 | :A1:
9 |
10 | Set ``playback_settings`` when you create the environment. After finishing a game, ``test.pb`` will be saved at ``save_dir``, which can be shown by the GoBigger replayer.
11 |
12 | .. code-block::
13 |
14 | env = create_env('st_t3p2', dict(
15 | playback_settings=dict(
16 | playback_type='by_frame',
17 | by_frame=dict(
18 | save_frame=True,
19 | save_dir='.',
20 | save_name_prefix='test',
21 | ),
22 | ),
23 | ))
24 |
25 |
26 |
27 | Q2: What are the winning conditions at the end of the game?
28 | **********************************************************************
29 |
30 | :A2:
31 |
32 | Sort by calculating the sum of the scores of all players under each team at the end of the game.
33 |
34 |
35 | Q3: Is there a limit to the size of the partial field of view?
36 | **********************************************************************
37 |
38 | :A3:
39 |
40 | The size of the player's partial field of view is determined by the relative position of the player's doppelganger. We set the player’s minimum field of view to be a matrix of 36*36. With the dispersion of the doppelganger, the player's maximum field of vision can reach the global level.
41 |
--------------------------------------------------------------------------------
/docs/en/source/index.rst:
--------------------------------------------------------------------------------
1 | .. gobigger documentation master file, created by
2 | sphinx-quickstart on Fri Aug 27 11:46:57 2021.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to GoBigger's documentation!
7 | =========================================
8 |
9 | Overview
10 | ------------
11 | GoBigger is a **multi-agent** environment for reinforce learning. It is similar to `Agar `_, which is a massively multiplayer online action game created by Brazilian developer Matheus Valadares. In GoBigger, players control one or more circular balls in a map. The goal is to gain as much size as possible by eating food balls and other balls smaller than the player's balls while avoiding larger ones which can eat the player's balls. Each player starts with one ball, but players can split a ball into two once it reaches a sufficient size, allowing them to control multiple balls.
12 |
13 | GoBigger allows users to interact with the multi-agent environment easily. Through the given interface, users can get observations by actions created by their policy. Users can also customize their game environments by changing the args in the config.
14 |
15 | Indices and tables
16 | =====================
17 |
18 | .. toctree::
19 | :maxdepth: 2
20 | :caption: Tutorial
21 |
22 | installation/index
23 | tutorial/quick_start
24 | tutorial/what_is_gobigger
25 | tutorial/gobigger_engine
26 | tutorial/real_time_interaction_with_game
27 | tutorial/space
28 | tutorial/gobigger_env
29 | tutorial/playback
30 |
31 | .. toctree::
32 | :maxdepth: 2
33 | :caption: Advanced
34 |
35 | advanced/cfg_intro
36 | advanced/collision
37 |
38 | .. toctree::
39 | :maxdepth: 2
40 | :caption: Commutity
41 |
42 | community/faq
43 |
44 | .. toctree::
45 | :maxdepth: 2
46 | :caption: API Docs
47 |
48 | api_doc/agents
49 | api_doc/balls
50 | api_doc/managers
51 | api_doc/players
52 | api_doc/render
53 | api_doc/server
54 | api_doc/utils
55 |
--------------------------------------------------------------------------------
/docs/en/source/installation/index.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ##############
3 |
4 | Prerequisites
5 | =================
6 |
7 | System version:
8 |
9 | * Centos 7
10 | * Windows 10
11 | * MacOS
12 |
13 | Python version: 3.6.8
14 |
15 | Get and install GoBigger
16 | =============================
17 |
18 | You can simply install GoBigger from PyPI with the following command:
19 |
20 | .. code-block:: bash
21 |
22 | pip install gobigger
23 |
24 |
25 | If you use Anaconda or Miniconda, you can install GoBigger through the following command:
26 |
27 | .. code-block:: bash
28 |
29 | conda install -c opendilab gobigger
30 |
31 |
32 | You can also install with newest version through GitHub. First get and download the official repository with the following command line.
33 |
34 | .. code-block:: bash
35 |
36 | git clone https://github.com/opendilab/GoBigger.git
37 |
38 | Then you can install from source:
39 |
40 | .. code-block:: bash
41 |
42 | # install for use
43 | # Note: use `--user` option to install the related packages in the user own directory(e.g.: ~/.local)
44 | pip install . --user
45 |
46 | # install for development(if you want to modify GoBigger)
47 | pip install -e . --user
48 |
49 |
50 |
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/eat_food.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/eat_food.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/eat_player.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/eat_player.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/eject.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/eject.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/eject_and_move.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/eject_and_move.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/eject_cross.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/eject_cross.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/eject_merger.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/eject_merger.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/eject_to_thorns.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/eject_to_thorns.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/fast_eat.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/fast_eat.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/merge_quickly.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/merge_quickly.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/on_thorns.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/on_thorns.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/overview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/overview.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/split.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/split.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/split_eat_all.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/split_eat_all.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/images/split_merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/en/source/tutorial/images/split_merge.gif
--------------------------------------------------------------------------------
/docs/en/source/tutorial/index.rst:
--------------------------------------------------------------------------------
1 | Tutorial
2 | ##########
3 |
4 | .. toctree::
5 | :maxdepth: 3
6 |
7 | quick_start
8 | what_is_gobigger
9 | gobigger_engine
10 | real_time_interaction_with_game
11 | space
12 | gobigger_env
13 | playback
14 |
--------------------------------------------------------------------------------
/docs/en/source/tutorial/playback.rst:
--------------------------------------------------------------------------------
1 | Playback System
2 | ##################
3 |
4 | GoBigger's playback system supports three options, which can be selected through the environment's configuration file. The configuration items involved are as follows:
5 |
6 | .. code-block:: python
7 |
8 | config = dict(
9 | ...
10 | playback_settings=dict(
11 | playback_type='none', # ['none', 'by_video', 'by_frame']
12 | by_video=dict(
13 | save_video=False,
14 | save_fps=10,
15 | save_resolution=552,
16 | save_all=True,
17 | save_partial=False,
18 | save_dir='.',
19 | save_name_prefix='test',
20 | ),
21 | by_frame=dict(
22 | save_frame=False,
23 | save_all=True,
24 | save_partial=False,
25 | save_dir='.',
26 | save_name_prefix='test',
27 | ),
28 | by_action=dict(
29 | save_action=False,
30 | save_dir='.',
31 | save_name_prefix='test',
32 | ),
33 | ),
34 | ...
35 | )
36 |
37 | ``playback_type`` can be one of ``['none', 'by_video', 'by_frame']``.
38 |
39 | * ``none``: means no need to save playback
40 | * ``by_video``: means to save the video directly, and the suffix of the saved file is ``.mp4``. Generally speaking, the video saved in the ``st_t4p3`` environment is about 80M.
41 | * ``by_frame``: Represents the change amount of each frame saved, and the suffix of the saved file is ``.pb``. Generally speaking, the ``st_t4p3`` environment saves files around 25M.
42 |
43 |
44 | Save video
45 | ----------------------------
46 |
47 | If ``playback_type='by_video'`` is selected, the specific configuration items can be as follows:
48 |
49 | .. code-block:: python
50 |
51 | env = create_env('st_t4p3', dict(
52 | playback_settings=dict(
53 | playback_type='by_video',
54 | by_video=dict(
55 | save_video=True,
56 | save_dir='.', # The directory location where the video needs to be saved
57 | save_name_prefix='test', # Save the prefix of the video name
58 | ),
59 | ),
60 | ))
61 |
62 |
63 | Save the pb file
64 | ----------------------------
65 |
66 | If ``playback_type='by_frame'`` is selected, the specific configuration items can be as follows:
67 |
68 | .. code-block:: python
69 |
70 | env = create_env('st_t4p3', dict(
71 | playback_settings=dict(
72 | playback_type='by_frame',
73 | by_frame=dict(
74 | save_frame=True,
75 | save_dir='.', # The directory location where the video needs to be saved
76 | save_name_prefix='test', # The prefix of the name of the saving video
77 | )
78 | ),
79 | ))
80 |
81 | After getting the saved ``.pb`` file, you need to view it through our given player. Execute the following command in the command line to open the player.
82 |
83 | .. code-block:: python
84 |
85 | python -m gobigger.bin.replayer
86 |
87 | After opening the player, you need to select the ``.pb`` file you want to view. Then you can start watching. The player supports double-speed playback, including 2x, 4x, and 8x (by clicking the button in the lower left corner). Also supports dragging the progress bar.
88 |
--------------------------------------------------------------------------------
/docs/en/source/tutorial/quick_start.rst:
--------------------------------------------------------------------------------
1 | Quick Start
2 | ##############
3 |
4 | Launch a game environment
5 | ==================================
6 |
7 | After installation, you can launch your game environment easily according the following code:
8 |
9 | .. code-block:: python
10 |
11 | import random
12 | from gobigger.envs import create_env
13 |
14 | env = create_env('st_t2p2')
15 | obs = env.reset()
16 | for i in range(1000):
17 | actions = {0: [random.uniform(-1, 1), random.uniform(-1, 1), 0],
18 | 1: [random.uniform(-1, 1), random.uniform(-1, 1), 0],
19 | 2: [random.uniform(-1, 1), random.uniform(-1, 1), 0],
20 | 3: [random.uniform(-1, 1), random.uniform(-1, 1), 0]}
21 | obs, rew, done, info = env.step(actions)
22 | print('[{}] leaderboard={}'.format(i, obs[0]['leaderboard']))
23 | if done:
24 | print('finish game!')
25 | break
26 | env.close()
27 |
28 | You will see output as following. It shows the frame number and the leaderboard per frame.
29 |
30 | .. code-block::
31 |
32 | [0] leaderboard={0: 3000, 1: 3100.0}
33 | [1] leaderboard={0: 3000, 1: 3100.0}
34 | [2] leaderboard={0: 3000, 1: 3100.0}
35 | [3] leaderboard={0: 3000, 1: 3100.0}
36 | [4] leaderboard={0: 3000, 1: 3100.0}
37 | [5] leaderboard={0: 3000, 1: 3100.0}
38 | [6] leaderboard={0: 3000, 1: 3100.0}
39 | [7] leaderboard={0: 3000, 1: 3100.0}
40 | [8] leaderboard={0: 3000, 1: 3100.0}
41 | [9] leaderboard={0: 3000, 1: 3100.0}
42 | [10] leaderboard={0: 3000, 1: 3100.0}
43 | ...
44 |
45 |
46 | Customize your config
47 | ============================
48 |
49 | Users can also choose to customize the game environment by modifying the configuration cfg and through the ``gobigger.envs.create_env_custom`` method we provide. The ``gobigger.envs.create_env_custom`` method accepts two parameters, the first parameter is ``type``, the optional value is ``st`` or ``sp``, which represent the standard game mode, and the independent action game mode. See the introduction of the two modes for details. Below we give a few simple examples based on the standard game mode.
50 |
51 | Add more players in a game
52 | ------------------------------------
53 |
54 | For example, you may want to allow 6 teams and 2 players per team in your game, and then please modify ``team_num`` and ``player_num_per_team`` in config.
55 |
56 | .. code-block:: python
57 |
58 | from gobigger.envs import create_env_custom
59 |
60 | env = create_env_custom(type='st', cfg=dict(
61 | team_num=6,
62 | player_num_per_team=2
63 | ))
64 |
65 | Extend the game time
66 | ------------------------------------
67 | If you want to extend the game time to 20 minutes (24000 frames), you can use the following codes.
68 |
69 | .. code-block:: python
70 |
71 | from gobigger.envs import create_env_custom
72 |
73 | env = create_env_custom(type='st', cfg=dict(
74 | frame_limit=24000
75 | ))
76 |
77 | Change the size of the map
78 | ------------------------------------
79 |
80 | If you want to have a larger map, you can change ``map_width`` and ``map_height`` in config.
81 |
82 | .. code-block:: python
83 |
84 | from gobigger.envs import create_env_custom
85 |
86 | env = create_env_custom(type='st', cfg=dict(
87 | map_width=1000,
88 | map_height=1000,
89 | ))
90 |
91 |
92 |
--------------------------------------------------------------------------------
/docs/en/source/tutorial/real_time_interaction_with_game.rst:
--------------------------------------------------------------------------------
1 | Real-time Interaction with game
2 | ##########################################
3 |
4 | GoBigger allow users to play game on their personal computer in real-time. Serveral modes are supported for users to explore this game.
5 |
6 | .. note::
7 |
8 | If your version of GoBigger is v0.1.x, please upgrade for a better experience. You can use ``pip install --upgrade gobigger`` to access the latest version of GoBigger.
9 |
10 |
11 | Standard setting with partial view
12 | --------------------------------------
13 |
14 | If you want to play real-time game on your PC on your own, you can launch a game with the following code:
15 |
16 | .. code-block:: bash
17 |
18 | python -m gobigger.bin.play --mode st --vision-type partial
19 |
20 | In this mode, using mouse to control the your balls to move, ``Q`` means eject spore on your moving direction, ``W`` means split your balls, and ``E`` means stop all your balls and gather them together.
21 |
22 |
23 | Standard setting with full view
24 | --------------------------------------
25 |
26 | If you want to play real-time game on your PC with your friends, you can launch a game with the following code:
27 |
28 | .. code-block:: bash
29 |
30 | python -m gobigger.bin.play --mode st --vision-type full
31 |
32 |
33 | Independent action setting with partial view
34 | ------------------------------------------------
35 |
36 | In the independent action game mode, each ball of the player receives an action individually. But because it is more difficult to control, we still only receive an action based on the mouse and keyboard in this mode, and assign this action to all the player's balls. The game can be started with the following code:
37 |
38 | .. code-block:: bash
39 |
40 | python -m gobigger.bin.play --mode sp --vision-type partial
41 |
42 |
--------------------------------------------------------------------------------
/docs/zh_CN/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/zh_CN/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/zh_CN/requirements.txt:
--------------------------------------------------------------------------------
1 | easydict
2 | gym
3 | pygame
4 | pytest
5 | opencv-python
6 | numpy
7 | sphinx
8 | tqdm
9 | trueskill
10 | git+http://github.com/opendilab/GoBigger@main
11 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/_static/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;
3 | }
4 |
5 | /* Default header fonts are ugly */
6 | h1, h2, .rst-content .toctree-wrapper p.caption, h3, h4, h5, h6, legend, p.caption {
7 | font-family: "Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;
8 | }
9 |
10 | /* Use white for docs background */
11 | .wy-side-nav-search {
12 | background-color: #fff;
13 | }
14 |
15 | .wy-nav-content {
16 | max-width: 1200px !important;
17 | }
18 |
19 | .wy-nav-content-wrap, .wy-menu li.current > a {
20 | background-color: #fff;
21 | }
22 |
23 | .wy-side-nav-search>a img.logo {
24 | width: 80%;
25 | margin-top: 10px;
26 | }
27 |
28 | @media screen and (min-width: 1400px) {
29 | .wy-nav-content-wrap {
30 | background-color: #fff;
31 | }
32 |
33 | .wy-nav-content {
34 | background-color: #fff;
35 | }
36 | }
37 |
38 | /* Fixes for mobile */
39 | .wy-nav-top {
40 | background-color: #fff;
41 | /*background-image: url('../images/tianshou-logo.png');*/
42 | background-repeat: no-repeat;
43 | background-position: center;
44 | padding: 0;
45 | margin: 0.4045em 0.809em;
46 | color: #333;
47 | }
48 |
49 | .wy-nav-top > a {
50 | display: none;
51 | }
52 |
53 | @media screen and (max-width: 768px) {
54 | .wy-side-nav-search>a img.logo {
55 | height: 60px;
56 | }
57 | }
58 |
59 | /* This is needed to ensure that logo above search scales properly */
60 | .wy-side-nav-search a {
61 | display: block;
62 | }
63 |
64 | /* This ensures that multiple constructors will remain in separate lines. */
65 | .rst-content dl:not(.docutils) dt {
66 | display: table;
67 | }
68 |
69 | /* Use our red for literals (it's very similar to the original color) */
70 | .rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal {
71 | color: #4692BC;
72 | }
73 |
74 | .rst-content tt.xref, a .rst-content tt, .rst-content tt.xref,
75 | .rst-content code.xref, a .rst-content tt, a .rst-content code {
76 | color: #404040;
77 | }
78 |
79 | /* Change link colors (except for the menu) */
80 |
81 | a {
82 | color: #4692BC;
83 | }
84 |
85 | a:hover {
86 | color: #4692BC;
87 | }
88 |
89 |
90 | a:visited {
91 | color: #4692BC;
92 | }
93 |
94 | .wy-menu a {
95 | color: #b3b3b3;
96 | }
97 |
98 | .wy-menu a:hover {
99 | color: #b3b3b3;
100 | }
101 |
102 | /* Default footer text is quite big */
103 | footer {
104 | font-size: 80%;
105 | }
106 |
107 | footer .rst-footer-buttons {
108 | font-size: 125%; /* revert footer settings - 1/80% = 125% */
109 | }
110 |
111 | footer p {
112 | font-size: 100%;
113 | }
114 |
115 | .ethical-rtd {
116 | display: none;
117 | }
118 |
119 | .ethical-fixedfooter {
120 | display: none;
121 | }
122 |
123 | .ethical-content {
124 | display: none;
125 | }
126 |
127 | /* For hidden headers that appear in TOC tree */
128 | /* see http://stackoverflow.com/a/32363545/3343043 */
129 | .rst-content .hidden-section {
130 | display: none;
131 | }
132 |
133 | nav .hidden-section {
134 | display: inherit;
135 | }
136 |
137 | .wy-side-nav-search>div.version {
138 | color: #000;
139 | }
--------------------------------------------------------------------------------
/docs/zh_CN/source/_templates/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "!layout.html" %}
2 | {% block extrahead %}
3 |
4 | {% endblock %}
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/cfg_intro.rst:
--------------------------------------------------------------------------------
1 | 游戏配置介绍
2 | ##############
3 |
4 |
5 | 总览
6 | ======================
7 |
8 | 为了让玩家更进一步了解游戏机制的实现,我们开放了部分参数供玩家进行调整。我们希望玩家可以通过修改对应的参数,来实现各种不同的环境。同时,也可以设计自己的代理环境,用于对算法的快速验证。
9 |
10 | GoBigger 将可配置参数统一放在 ``gobigger/server/server_default_config.py``。其中对可配置参数进行详细介绍。
11 |
12 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/collision.rst:
--------------------------------------------------------------------------------
1 | 碰撞检测算法
2 | ##############
3 |
4 | 总览
5 | ======================
6 |
7 | 为了在游戏的每一帧中检测球体的碰撞,以便更新球体的状态,我们需要设计高效的碰撞检测算法。因此,我们设计了四种碰撞检测算法,并将它们封装成以下四类。下面仅介绍算法相关结果,更多细节请查看源码。
8 |
9 | 算法介绍
10 | ======================
11 |
12 | 四种算法的介绍和理论时间复杂度如下:
13 |
14 | **ExhaustiveCollisionDetection:**
15 |
16 | :math:`O(n*m)`
17 |
18 | 暴力解法,其中 n 表示球的总数,m 表示要询问的球数。
19 |
20 | **PrecisionCollisionDetection:**
21 |
22 | :math:`O(n*log(n)+\Sigma{r}*logn+p)`
23 |
24 | 卡精度解法。其中 n 表示球的总数,m 表示要询问的球数,k 表示我们设置的精度,p 表示实际碰撞的球体数量。
25 |
26 | **RebuildQuadTreeCollisionDetection:**
27 |
28 | :math:`O(n*log(n) + m*log(n)+p)`
29 |
30 | 重建四叉树解法。其中 n 表示球的总数,m 表示要询问的球数,p 表示实际碰撞的球体数量。
31 |
32 | **RemoveQuadTreeCollisionDetection:**
33 |
34 | :math:`O(r*log(n)+m*log(n)+p)`
35 |
36 | 优化后的重建四叉树解法,增加了对四叉树的删除和维护。其中n表示球的总数,m表示要询问的球数,r表示位置状态发生变化的球体数量,p表示实际碰撞的球体数量。
37 |
38 | 为了测试这些算法的效率,我们修改了球总数、查询数、改变球数、迭代轮数等参数来设置测试场景。下表中的数据来自最具代表性的场景:
39 |
40 | * `T:地图中所有球的数量`
41 | * `Q:查询球的数量,通常表示地图中移动的球`
42 | * `C:需要修改的球的数量,即碰撞次数`
43 |
44 | +----------+------------+-----------+------------------+-----------------+
45 | | | Exhaustive | Precision | Rebuild QuadTree | Remove QuadTree |
46 | +==========+============+===========+==================+=================+
47 | | T=3000 | 688ms | 14ms | 47ms | 48ms |
48 | | | | | | |
49 | | Q=300 | | | | |
50 | | | | | | |
51 | | C=600 | | | | |
52 | +----------+------------+-----------+------------------+-----------------+
53 | | T=3000 | 1067ms | 16ms | 50ms | 178ms |
54 | | | | | | |
55 | | Q=300 | | | | |
56 | | | | | | |
57 | | C=1500 | | | | |
58 | +----------+------------+-----------+------------------+-----------------+
59 | | T=10000 | 8384ms | 61ms | 339ms | 497ms |
60 | | | | | | |
61 | | Q=1000 | | | | |
62 | | | | | | |
63 | | C=2000 | | | | |
64 | +----------+------------+-----------+------------------+-----------------+
65 | | T=10000 | 12426ms | 86ms | 586ms | 2460ms |
66 | | | | | | |
67 | | Q=2000 | | | | |
68 | | | | | | |
69 | | C=5000 | | | | |
70 | +----------+------------+-----------+------------------+-----------------+
71 | | T=30000 | 127000ms | 403ms | 5691ms | 8419ms |
72 | | | | | | |
73 | | Q=6000 | | | | |
74 | | | | | | |
75 | | C=3000 | | | | |
76 | +----------+------------+-----------+------------------+-----------------+
77 |
78 | 为了更直观的看到每种算法的优劣,我们整合了测试数据,绘制了四种算法和各种参数的图表如下:
79 |
80 | .. only:: html
81 |
82 | .. figure:: images/changed_num_3000.png
83 | :width: 600
84 | :align: center
85 |
86 | .. only:: html
87 |
88 | .. figure:: images/query_num_3000.png
89 | :width: 600
90 | :align: center
91 |
92 | .. only:: html
93 |
94 | .. figure:: images/iters_num_3000.png
95 | :width: 600
96 | :align: center
97 |
98 | 根据结果,我们可以认为 **PrecisionCollisionDetection** 算法在效率和稳定性方面都远远优于其他算法。
99 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/images/2f2s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/advanced/images/2f2s.png
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/images/2f2s_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/advanced/images/2f2s_v2.png
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/images/2f2s_v3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/advanced/images/2f2s_v3.png
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/images/changed_num_3000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/advanced/images/changed_num_3000.png
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/images/eighth_merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/advanced/images/eighth_merge.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/images/iters_num_3000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/advanced/images/iters_num_3000.png
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/images/quarter_merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/advanced/images/quarter_merge.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/images/query_num_3000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/advanced/images/query_num_3000.png
--------------------------------------------------------------------------------
/docs/zh_CN/source/advanced/images/straight_merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/advanced/images/straight_merge.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/community/faq.rst:
--------------------------------------------------------------------------------
1 | FAQ
2 | ##############
3 |
4 |
5 | Q1: 如何保存对局录像?
6 | ***********************************
7 |
8 | :A1:
9 |
10 | 创建 env 的时候传入 ``playback_settings`` 相关参数,如下图所示。这样在**一局结束**之后会在 ``save_dir`` 目录下保存 ``test.pb`` 文件。通过 GoBigger 提供播放器可以看这局比赛。
11 |
12 | .. code-block::
13 |
14 | env = create_env('st_t3p2', dict(
15 | playback_settings=dict(
16 | playback_type='by_frame',
17 | by_frame=dict(
18 | save_frame=True,
19 | save_dir='.',
20 | save_name_prefix='test',
21 | ),
22 | ),
23 | ))
24 |
25 |
26 | Q2: 比赛最后的获胜条件是什么?
27 | ***********************************
28 |
29 | :A2:
30 |
31 | 通过计算比赛结束时每个队伍下所有玩家的得分和来进行排序。
32 |
33 |
34 | Q3: 局部视野的大小有范围限制吗?
35 | ***********************************
36 |
37 | :A3:
38 |
39 | 玩家的局部视野的大小由其分身球的相对位置决定。我们设置玩家的最小视野是 36*36 的一个矩阵。随着分身球的分散,玩家的最大视野可以达到全局的程度。
40 |
41 |
42 | Q4: conda环境(使用的推荐的python3.6.8)下安装了 gobigger,实际运行的时候出现 ``libGL error failed to open iris`` 该怎么办
43 | ***********************************
44 |
45 | :A4:
46 |
47 | 是glibc版本过低却安装了高版本的libgl导致的。这里有个相似的问题可以看看 https://askubuntu.com/questions/1352158/libgl-error-failed-to-load-drivers-iris-and-swrast-in-ubuntu-20-04
48 |
49 |
50 | Q5: 该环境中,智能体能否执行出类似于人类玩家的中吐行为?周围自己的小球把孢子吐给中间的球?
51 | ***********************************
52 |
53 | :A5: 在执行吐孢子操作的时候,可以指定方向。我们把和方向有关的参数 ``(x, y)`` 设置为 ``(0, 0)``,则玩家球会逐渐减速,同时方向会慢慢转向质心。之后再执行吐孢子的动作,则会实现中吐。
54 |
55 | .. code-block::
56 |
57 | action1 = [0, 0, -1] # 停止
58 | action2 = [0, 0, 2] # 吐孢子
59 |
60 |
61 | Q6: 吃荆棘球,自己体积会变大吗?还是说只分裂?
62 | ***********************************
63 |
64 | :A6: 体积也会变大。
65 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/index.rst:
--------------------------------------------------------------------------------
1 | .. gobigger documentation master file, created by
2 | sphinx-quickstart on Fri Aug 27 11:46:57 2021.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | 欢迎查阅 GoBigger 中文文档!
7 | =========================================
8 |
9 | 总览
10 | ------------
11 |
12 | 多智能体对抗作为决策AI中重要的部分,也是强化学习领域的难题之一。为丰富多智能体对抗环境, OpenDILab 开源了一款多智能体对抗竞技游戏环境——Go-Bigger。同时,Go-Bigger 还可作为强化学习环境协助多智能体决策 AI 研究。
13 | 与风靡全球的 `Agar `_ 等游戏类似,在 Go-Bigger 中,玩家(AI)控制地图中的一个或多个圆形球,通过吃食物球和其他比玩家球小的单位来尽可能获得更多重量,并需避免被更大的球吃掉。每个玩家开始仅有一个球,当球达到足够大时,玩家可使其分裂、吐孢子或融合,和同伴完美配合来输出博弈策略,并通过AI技术来操控智能体由小到大地进化,凭借对团队中多智能体的策略控制来吃掉尽可能多的敌人,从而让己方变得更强大并获得最终胜利。
14 |
15 | GoBigger 提供了多种接口供用户方便快捷地与游戏环境进行交互。如果想快速了解游戏,可以通过实时人机对战接口在本地快速地开启一局游戏。同时 GoBigger 还提供了方便的标准 gym.Env 的接口以供研究人员学习他们的策略。用户也可以自定义游戏的初始环境,来进行更多样化的游戏对局。
16 |
17 |
18 | 索引
19 | =====================
20 |
21 | .. toctree::
22 | :maxdepth: 2
23 | :caption: 教程
24 |
25 | installation/index
26 | tutorial/quick_start
27 | tutorial/what_is_gobigger
28 | tutorial/gobigger_engine
29 | tutorial/real_time_interaction_with_game
30 | tutorial/space
31 | tutorial/gobigger_env
32 | tutorial/playback
33 |
34 | .. toctree::
35 | :maxdepth: 2
36 | :caption: 进阶
37 |
38 | advanced/cfg_intro
39 | advanced/collision
40 |
41 | .. toctree::
42 | :maxdepth: 2
43 | :caption: 社区
44 |
45 | community/faq
46 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/installation/index.rst:
--------------------------------------------------------------------------------
1 | 安装
2 | ##############
3 |
4 | 前置需求
5 | =================
6 |
7 | 我们已经在以下系统版本中进行过测试:
8 |
9 | * Centos 7
10 | * Windows 10
11 | * MacOS
12 |
13 | 同时,我们推荐使用 Python 版本为 ``3.6.8``。
14 |
15 |
16 | 快速安装 GoBigger
17 | =============================
18 |
19 | 我们可以通过 PyPI 直接安装:
20 |
21 | .. code-block:: bash
22 |
23 | pip install gobigger
24 |
25 | 同样,也可以通过 conda 进行安装:
26 |
27 | .. code-block:: bash
28 |
29 | conda install -c opendilab gobigger
30 |
31 | 如果想要使用最新的版本,可以通过源码进行安装。首先,通过 Github 下载 GoBigger 源码。
32 |
33 | .. code-block:: bash
34 |
35 | git clone https://github.com/opendilab/GoBigger.git
36 |
37 | 然后,我们从源代码进行安装。
38 |
39 | .. code-block:: bash
40 |
41 | # install for use
42 | # Note: use `--user` option to install the related packages in the user own directory(e.g.: ~/.local)
43 | pip install . --user
44 |
45 | # install for development(if you want to modify GoBigger)
46 | pip install -e . --user
47 |
48 |
49 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/gobigger_engine.rst:
--------------------------------------------------------------------------------
1 | GoBigger 引擎设计
2 | ####################
3 |
4 | 总览
5 | ===============
6 |
7 | 本节主要说明了 GoBigger 的引擎设计,其中包括各种球的详细运动逻辑设定。如果读者想要根据 GoBigger 去开发新的环境,或者想要更深入了解 GoBigger 的研发细节,可以仔细阅读本节。为了方便阐述,我们将按顺序介绍每一种球的逻辑设定,并在其中穿插和其他球的交互过程。如果你对 GoBigger 中基础的球单位还不熟悉,建议先查阅上一节(GoBigger是什么)。注意,以下的说明均基于 ``st_t4p3`` 的设置。
8 |
9 |
10 | 所有球的共同点
11 | ==============
12 |
13 | 1. 在 GoBigger 中,你可以看到不同的球有不同的大小。我们定义每个球都有一定的分数 ``score``,并通过分数可以计算出对应的半径 ``radius``。
14 |
15 | .. code-block:: python
16 |
17 | import math
18 |
19 | def score_to_radius(score):
20 | return math.sqrt(score / 100 * 0.042 + 0.15)
21 |
22 | 2. 食物球是不可移动的,但是荆棘球,孢子球,分身球都包含速度属性,也就是说他们都可以移动。
23 | 3. 如果两个球存在可以吃与被吃的关系(例如分身球之间,分身球和所有球,荆棘球和孢子球之间),那么当两个球出现圆心重合的情况时,将会通过判断二者分数来判断是否能发生吞噬。我们规定当大球的分数超过小球的 ``1.3`` 倍时才能发生吞噬。
24 | 4. 所有球在运动过程中圆心都不会超出地图边界。
25 | 5. 我们默认游戏内的 ``FPS`` 是 ``20``。也就是说,一秒内游戏状态会更新 ``20`` 次。同时,默认情况下每次调用 ``env.step`` 会使得环境前进两帧,用户提供的动作会在第一帧做完,然后第二帧赋予空动作。
26 |
27 |
28 | 食物球
29 | ===============
30 |
31 | 1. 食物球是游戏中的中立资源。在 ``st_t4p3`` 的配置中,开局时地图上的食物球数量会有 ``800`` 个,且数量上限为 ``900`` 个。每隔 ``8`` 帧,将会补充 ``(数量上限-当前数量)* 0.01`` 个食物球,并使得食物球的数量不会超过数量上限。
32 | 2. 食物球的初始分数是 ``100``。也就是说,如果一个分身球吃掉了一个食物球,那么这个分身球的分数会增加 ``100``。
33 |
34 |
35 | 荆棘球
36 | ===============
37 | 1. 也称为刺球。荆棘球也是游戏中的中立资源。在 ``st_t4p3`` 的配置中,开局时地图上的荆棘球数量会有 ``9`` 个,且数量上限为 ``12`` 个。每隔 ``120`` 帧,将会补充向上取整 ``(数量上限-当前数量)* 0.2`` 个荆棘球,并使得荆棘球的数量不会超过数量上限。
38 | 2. 荆棘球的初始分数会从 ``10000`` 到 ``15000`` 范围内随机选择。
39 | 3. 当玩家向荆棘球吐孢子球的时候,如果孢子球和荆棘球发生了圆心覆盖,荆棘球会吃掉孢子球,同时往孢子球的运动方向产生一个为 ``10`` 的初速度,并且该速度在 ``20`` 帧内均匀衰减到 ``0``。
40 |
41 |
42 | 孢子球
43 | ===============
44 | 1. 孢子球的大小是固定的,分数固定为 ``1400``。
45 | 2. 孢子球被吐出的时候速度是固定的,为 ``30``,并且该速度在 ``20`` 帧内均匀衰减到 ``0``。
46 |
47 |
48 | 分身球
49 | ===============
50 | 分身球的大小随着不断吃球而变大。分身球的初始分数为 ``1000``。单个玩家最多拥有 ``16`` 个分身球。每个分身球的分数超过 ``3200`` 时才可以使用吐孢子 ``eject`` 技能,超过 ``3600`` 时才可以使用分裂 ``split`` 技能。
51 |
52 |
53 | 速度
54 | ---------------
55 | 分身球的速度由三部分矢量共同作用:玩家操作,多个球存在时导致的向心力,以及分裂或吃荆棘球之后带来的逐渐衰减的速度。玩家操作和向心力带来的加速度在每一帧会乘以每帧时间长度来作为速度的改变量。
56 |
57 | 1. 玩家操作:如动作空间定义的那样,玩家可以提供一个单位圆内的任意一点 ``(x,y)`` 来改变分身球的速度。具体来说,GoBigger 会首先将这一点归一化到单位圆内(如果在圆外则归一化到圆上,否则不做处理),然后乘以权重 ``30`` 来作为加速度。
58 | 2. 向心力:当玩家拥有多个分身球的时候,多个球内部会产生一个向心力,该力指向质心。实际上向心力不会直接使用,会除以半径之后作为真正的向心力,并乘以权重 ``10`` 来作为加速度。
59 | 3. 分裂或吃荆棘球之后带来的逐渐衰减的速度:分身球吃掉荆棘球分裂出来的新的分身球会拥有一个分裂后新的初始速度。这个速度的模长为和分裂后的半径有关,具体为 ``(260 - 20 * radius) / 7``,方向背离圆心。该速度会在 ``14`` 帧内均匀衰减到 ``0``。如果分身球是通过分裂技能得到,那这个初始速度的模长的计算公式为 ``(95 + 19 * radius) / 7``。
60 | 4. 玩家操作和向心力带来的速度会受到速度上限的限制。速度上限和球的半径有关,并会将玩家操作和向心力中较大之一来作为 ``ratio`` 对速度上限进行调整。具体公式为 ``(2.35 + 5.66 / radius) * ratio``。
61 |
62 |
63 | 吃荆棘球
64 | ---------------
65 | 1. 分身球每次吃掉荆棘球的时候,会分裂出不超过上限 ``10`` 个新的分身球。举个例子,如果玩家当前只有两个分身球,而其中一个分身球吃掉了一个荆棘球,那他会分裂出新的 ``10`` 个分身球,因此此时该玩家总共有 ``12`` 个分身球;如果玩家当前有 ``10`` 个分身球,而其中一个分身球吃掉了一个荆棘球,那他会分裂出新的 ``6`` 个分身球,因此此时该玩家总共有 ``16`` 个分身球。
66 | 2. 分身球吃掉荆棘球分裂出来的新球的最大分数为 ``5000``。举个例子,分身球当前分数是 ``23000``,他吃掉了一个分数为 ``10000`` 的荆棘球,那么他的分数就会变成 ``33000``。与此同时,这个分身球会分裂出新的 ``10`` 个分身球,按照均匀分配,每个分身球的分数为 ``3000``。
67 | 3. 吃荆棘球分裂之后,新增的球的位置均匀分布在周围,且总会有新增的球分身处于原始球右侧水平位置。
68 |
69 |
70 | 分裂
71 | ---------------
72 | 1. 分裂出来新的分身球会在玩家指定的方向上,如果没有指定方向,则会出现在原始球的运动方向上。
73 | 2. 分裂技能会将原始球的分数均分到两个分裂后的球上。
74 | 3. 分裂出来新的分身球也会拥有原始分身球的速度。
75 | 4. 无论是分裂后还是吃荆棘球后,玩家的分身球(包括触发分裂和吃荆棘球,以及这俩操作后新增的分身球)都会进入长度为 ``20`` 秒的冷却阶段。在冷却阶段内的分身球无法触发和自己其他的分身球合并的操作。此外,在分身球完成一次合并之后,会重新进入冷却阶段。
76 |
77 |
78 | 吐孢子
79 | ---------------
80 | 1. 分身球执行吐孢子操作后产生的孢子球会出现在玩家指定的方向上,如果没有指定方向,则会出现在分身球的运动方向上。
81 | 2. 分身球每吐一个孢子球,相当于把自身分数中割离了 ``1400`` 分到新的孢子球上。
82 |
83 |
84 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/gobigger_env.rst:
--------------------------------------------------------------------------------
1 | GoBigger 环境
2 | ####################
3 |
4 | 总览
5 | ===============
6 |
7 | 本节主要说明了 GoBigger 的环境设计,其中包括给定好的环境的定义,以及如何自定义生成对应的环境。
8 |
9 |
10 | 已经定义好的环境
11 | =======================
12 |
13 | GoBigger 定义了一些基础环境,可以通过下面的代码生成这些基础环境
14 |
15 | .. code-block:: python
16 |
17 | from gobigger.envs import create_env
18 | env = create_env('st_t4p3')
19 |
20 | 其中,``create_env()`` 接收的第一个参数是一个字符串,是我们已经定义好的环境的名称。类似的,除了 ``st_t4p3`` 以外,我们还有 ``st_t2p2``,``st_t3p2`` 可供选择。下面详细介绍一下各个环境的区别。
21 |
22 | 1. ``st_t4p3``: 创建了一个有四支队伍,每支队伍三个玩家的游戏场景。这是我们定义的标准游戏场景,包括其中的各种球的刷新速度等参数都已经调节至一个比较健壮的水平。用户可以使用这个环境来解决一些复杂空间场景下的合作和对抗问题。
23 |
24 | 2. ``st_t2p2``: 考虑到 ``st_t4p3`` 可能过于庞大,因此我们提供了一个更小的游戏场景。在这个场景内,只有两支队伍,每支队伍两个玩家。同样,地图尺寸,食物球和荆棘球的数量也受到了相应的削减。此外,为了缩减玩家前期发育的时间,我们在 ``st_t2p2`` 将玩家第一次出生时的分数设置成了 13000 来使得玩家可以快速进行对抗,而这个值在 ``st_t4p3`` 中是 1000。
25 |
26 | 3. ``st_t3p2``: 这是一个间于 ``st_t2p2`` 和 ``st_t4p3`` 的中等环境,有三支队伍,每支队伍两个玩家。和 ``st_t2p2`` 一样,也设置了较高的出生分数来减少发育时间。
27 |
28 | 此外,上述环境默认都是每两帧做一个动作(一秒20帧)。如果想要更长的动作间隔,可以通过下面代码设置:
29 |
30 | .. code-block:: python
31 |
32 | from gobigger.envs import create_env
33 | env = create_env('st_t4p3', step_mul=10)
34 |
35 | 此外,我们还有更多已经定义好的环境,如下:
36 |
37 | +----------+--------------+-----------+--------------+----------+------------+----------------+
38 | | Name | Agents Size | Map Size | Food | Thorn | Init Size | Limited Frame |
39 | +==========+==============+===========+==============+==========+============+================+
40 | | Small Maps |
41 | +----------+--------------+-----------+--------------+----------+------------+----------------+
42 | | st_t1p1 | 1x1 | 32x32 | [65,75] | [1,2] | 1000 | 3600(3min) |
43 | +----------+--------------+-----------+--------------+----------+------------+----------------+
44 | | st_t1p2 | 1x2 | 48x48 | [130,150] | [2,3] | 1000 | 3600(3min) |
45 | +----------+--------------+-----------+--------------+----------+------------+----------------+
46 | | st_t2p1 | 2x1 | 48x48 | [130,150] | [2,3] | 1000 | 3600(3min) |
47 | +----------+--------------+-----------+--------------+----------+------------+----------------+
48 | | st_t2p2 | 2x2 | 64x64 | [260,300] | [3,4] | 13000 | 3600(3min) |
49 | +----------+--------------+-----------+--------------+----------+------------+----------------+
50 | | st_t3p2 | 3x2 | 88x88 | [500,560] | [5,6] | 13000 | 3600(3min) |
51 | +----------+--------------+-----------+--------------+----------+------------+----------------+
52 | | Large Maps |
53 | +----------+--------------+-----------+--------------+----------+------------+----------------+
54 | | st_t4p3 | 4x3 | 128x128 | [800,900] | [9,12] | 1000 | 14400(12min) |
55 | +----------+--------------+-----------+--------------+----------+------------+----------------+
56 | | st_t5p3 | 5x3 | 128x128 | [900,1000] | [10,12] | 1000 | 14400(12min) |
57 | +----------+--------------+-----------+--------------+----------+------------+----------------+
58 | | st_t5p4 | 5x4 | 144x144 | [900,1000] | [10,12] | 1000 | 14400(12min) |
59 | +----------+--------------+-----------+--------------+----------+------------+----------------+
60 | | st_t6p4 | 6x4 | 144x144 | [1000,1100] | [11,13] | 1000 | 14400(12min) |
61 | +----------+--------------+-----------+--------------+----------+------------+----------------+
62 |
63 |
64 | 自定义生成对应的环境
65 | =======================
66 |
67 | GoBigger 丰富的配置文件设计使得用户可以很方便地设置游戏内的每一个细节。
68 |
69 | 如果想要在某个我们已经定义好的环境的基础上进行简单修改,例如在 ``st_t2p2`` 修改一局时长,可以如下:
70 |
71 | .. code-block:: python
72 |
73 | from gobigger.envs import create_env
74 | env = create_env('st_t2p2', dict(
75 | frame_limit=10*60*20,
76 | ))
77 |
78 | 这样,开启的新环境就会在 ``st_t2p2`` 的基础上变成了 10 分钟一局。
79 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/eat_food.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/eat_food.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/eat_player.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/eat_player.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/eject.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/eject.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/eject_and_move.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/eject_and_move.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/eject_cross.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/eject_cross.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/eject_merger.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/eject_merger.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/eject_to_thorns.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/eject_to_thorns.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/fast_eat.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/fast_eat.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/merge_quickly.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/merge_quickly.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/on_thorns.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/on_thorns.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/overview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/overview.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/split.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/split.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/split_eat_all.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/split_eat_all.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/images/split_merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/docs/zh_CN/source/tutorial/images/split_merge.gif
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/index.rst:
--------------------------------------------------------------------------------
1 | 教程
2 | ##########
3 |
4 | .. toctree::
5 | :maxdepth: 3
6 |
7 | quick_start
8 | what_is_gobigger
9 | gobigger_engine
10 | real_time_interaction_with_game
11 | space
12 | gobigger_env
13 | playback
14 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/playback.rst:
--------------------------------------------------------------------------------
1 | 回放系统
2 | ##############
3 |
4 | GoBigger的回放系统支持三种选择,可以通过环境的配置文件来进行选择。涉及到的配置项如下:
5 |
6 | .. code-block:: python
7 |
8 | config = dict(
9 | ...
10 | playback_settings=dict(
11 | playback_type='none', # ['none', 'by_video', 'by_frame']
12 | by_video=dict(
13 | save_video=False,
14 | save_fps=10,
15 | save_resolution=552,
16 | save_all=True,
17 | save_partial=False,
18 | save_dir='.',
19 | save_name_prefix='test',
20 | ),
21 | by_frame=dict(
22 | save_frame=False,
23 | save_all=True,
24 | save_partial=False,
25 | save_dir='.',
26 | save_name_prefix='test',
27 | ),
28 | by_action=dict(
29 | save_action=False,
30 | save_dir='.',
31 | save_name_prefix='test',
32 | ),
33 | ),
34 | ...
35 | )
36 |
37 | ``playback_type`` 可以是 ``['none', 'by_video', 'by_frame']`` 中的其中一种。其中,
38 |
39 | * ``none``: 代表不需要存回放
40 | * ``by_video``: 代表直接保存录像,保存文件后缀是 ``.mp4``。一般来说,``st_t4p3`` 环境存下来的录像在 80M 左右。
41 | * ``by_frame``: 代表存每一帧的变化量,保存文件后缀是 ``.pb``。一般来说,``st_t4p3`` 环境存下来文件在 25M 左右。
42 |
43 |
44 | 直接保存录像
45 | --------------
46 |
47 | 如果选择 ``playback_type='by_video'``,具体的配置项可以像下面这样:
48 |
49 | .. code-block:: python
50 |
51 | env = create_env('st_t4p3', dict(
52 | playback_settings=dict(
53 | playback_type='by_video',
54 | by_video=dict(
55 | save_video=True,
56 | save_dir='.', # 需要保存录像的目录位置
57 | save_name_prefix='test', # 保存录像名字的前缀
58 | ),
59 | ),
60 | ))
61 |
62 |
63 | 直接保存pb文件
64 | --------------
65 |
66 | 如果选择 ``playback_type='by_frame'``,具体的配置项可以像下面这样:
67 |
68 | .. code-block:: python
69 |
70 | env = create_env('st_t4p3', dict(
71 | playback_settings=dict(
72 | playback_type='by_frame',
73 | by_frame=dict(
74 | save_frame=True,
75 | save_dir='.', # 需要保存录像的目录位置
76 | save_name_prefix='test', # 保存录像名字的前缀
77 | )
78 | ),
79 | ))
80 |
81 | 得到保存后的 ``.pb`` 文件之后,需要通过我们给定的播放器来查看。在命令行中执行下面的命令来打开播放器。
82 |
83 | .. code-block:: python
84 |
85 | python -m gobigger.bin.replayer
86 |
87 | 打开播放器之后,需要选择你想要查看的 ``.pb`` 文件。然后就可以开始看了。播放器支持倍速播放,包括2倍,4倍,8倍(通过点击左下角的按钮)。同时支持拖动进度条。
88 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/quick_start.rst:
--------------------------------------------------------------------------------
1 | 快速开始
2 | ##############
3 |
4 | 通过代码与游戏环境进行交互
5 | ==================================
6 |
7 | 在安装完成之后,可以用以下代码快速实现与游戏环境进行交互:
8 |
9 | .. code-block:: python
10 |
11 | import random
12 | from gobigger.envs import create_env
13 |
14 | env = create_env('st_t2p2')
15 | obs = env.reset()
16 | for i in range(1000):
17 | actions = {0: [random.uniform(-1, 1), random.uniform(-1, 1), 0],
18 | 1: [random.uniform(-1, 1), random.uniform(-1, 1), 0],
19 | 2: [random.uniform(-1, 1), random.uniform(-1, 1), 0],
20 | 3: [random.uniform(-1, 1), random.uniform(-1, 1), 0]}
21 | obs, rew, done, info = env.step(actions)
22 | print('[{}] leaderboard={}'.format(i, obs[0]['leaderboard']))
23 | if done:
24 | print('finish game!')
25 | break
26 | env.close()
27 |
28 | 在上述代码中,首先构建了环境,然后通过 ``env.step()`` 完成游戏每一步的进行,并获取到对应的 ``observation``,``reward``,``done``,``info`` 等信息。执行之后,将会得到类似下面的输出,给出了每一帧排行榜信息。
29 |
30 | .. code-block::
31 |
32 | [0] leaderboard={0: 3000, 1: 3100.0}
33 | [1] leaderboard={0: 3000, 1: 3100.0}
34 | [2] leaderboard={0: 3000, 1: 3100.0}
35 | [3] leaderboard={0: 3000, 1: 3100.0}
36 | [4] leaderboard={0: 3000, 1: 3100.0}
37 | [5] leaderboard={0: 3000, 1: 3100.0}
38 | [6] leaderboard={0: 3000, 1: 3100.0}
39 | [7] leaderboard={0: 3000, 1: 3100.0}
40 | [8] leaderboard={0: 3000, 1: 3100.0}
41 | [9] leaderboard={0: 3000, 1: 3100.0}
42 | [10] leaderboard={0: 3000, 1: 3100.0}
43 | ...
44 |
45 |
46 | 自定义游戏环境
47 | ============================
48 |
49 | 用户也可以选择通过修改配置 cfg,并通过我们提供的 ``gobigger.envs.create_env_custom`` 方法来自定义游戏环境。``gobigger.envs.create_env_custom`` 方法接收两个参数,第一个参数是 ``type``,可选值为 ``st`` 或 ``sp``,分别代表标准比赛模式,和独立动作比赛模式。关于两种模式的介绍具体可以看。以下我们基于标准比赛模式举几个简单的例子。
50 |
51 | 修改游戏中的队伍数量和玩家数量
52 | ------------------------------------
53 |
54 | 如果用户想要在游戏中存在 6 个队伍,每个队伍中含有 2 名玩家,那么可以修改 ``team_num`` 和 ``player_num_per_team``。
55 |
56 | .. code-block:: python
57 |
58 | from gobigger.envs import create_env_custom
59 |
60 | env = create_env_custom(type='st', cfg=dict(
61 | team_num=6,
62 | player_num_per_team=2
63 | ))
64 |
65 | 修改游戏时长
66 | ------------------------------------
67 |
68 | 游戏内默认每秒20帧。如果用户想要将游戏时长设置为 20 分钟,即 24000 帧,可以修改 ``frame_limit``。
69 |
70 | .. code-block:: python
71 |
72 | from gobigger.envs import create_env_custom
73 |
74 | env = create_env_custom(type='st', cfg=dict(
75 | frame_limit=24000
76 | ))
77 |
78 | 修改游戏地图大小
79 | ------------------------------------
80 |
81 | 如果用户想要拥有一个更大的地图,可以修改 ``map_width`` 和 ``map_height``。注意更大的地图可能会导致 ``step`` 速度变慢。
82 |
83 | .. code-block:: python
84 |
85 | from gobigger.envs import create_env_custom
86 |
87 | env = create_env_custom(type='st', cfg=dict(
88 | map_width=1000,
89 | map_height=1000,
90 | ))
91 |
92 |
93 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/real_time_interaction_with_game.rst:
--------------------------------------------------------------------------------
1 | 实时游玩
2 | ##########################################
3 |
4 | GoBigger 允许用户在个人电脑中实时游玩。同时也提供了多种游戏模式供用户选择。
5 |
6 | .. note::
7 |
8 | 如果你还在使用 GoBigger v0.1.x 系列,建议升级以获得更好的体验。可以使用 ``pip install --upgrade gobigger`` 来快速获取最新版本的 GoBigger。
9 |
10 |
11 | 标准比赛模式+部分视野
12 | =======================
13 |
14 | 该模式下,玩家只能看到周围一定范围内的视野。可以通过以下代码启动游戏:
15 |
16 | .. code-block:: bash
17 |
18 | python -m gobigger.bin.play --mode st --vision-type partial
19 |
20 | 在本模式中,鼠标可以用来操控玩家控制的球,三个技能分别是 ``Q``,``W``。``Q`` 技能是在移动方向上吐孢子,``W`` 技能是将用户的球进行分裂。玩家的视野由球的相对位置决定。尝试分散玩家的球,这样可以获得更大的视野。
21 |
22 |
23 | 标准比赛模式+全局视野
24 | =======================
25 |
26 | 可以通过以下代码启动游戏:
27 |
28 | .. code-block:: bash
29 |
30 | python -m gobigger.bin.play --mode st --vision-type full
31 |
32 |
33 | 独立动作比赛模式+部分视野
34 | =======================
35 |
36 | 独立动作比赛模式下,玩家的每个球都单独接受一个动作。但是由于比较难操控,我们在这个模式下还是只根据鼠标和键盘接收一个动作,并将这个动作赋给玩家的所有球。可以通过以下代码来启动游戏:
37 |
38 | .. code-block:: bash
39 |
40 | python -m gobigger.bin.play --mode sp --vision-type partial
41 |
--------------------------------------------------------------------------------
/docs/zh_CN/source/tutorial/what_is_gobigger.rst:
--------------------------------------------------------------------------------
1 | GoBigger 是什么
2 | ####################
3 |
4 | 总览
5 | ===============
6 |
7 | GoBigger 是一个类 `Agar `_ 游戏,后者是一款风靡全球的游戏。在 GoBigger 中,玩家需要在一张平面图中操控他的分身球。在地图中,还会有食物球,荆棘球,孢子球,以及其他的玩家的分身球。在有限的时间内,玩家需要通过吃掉其他的球来吸收他们的质量,并转化为自己的质量。在比赛结束的时候,质量最大的玩家将会获得胜利。为了提高游戏的对抗性,玩家还可以吃掉其他比他小的玩家来快速发育。因此,在游戏中,玩家需要兼顾快速发育和躲避风险,从而一步步获取到最多的质量以获得游戏的胜利。
8 |
9 | .. only:: html
10 |
11 | .. figure:: images/overview.gif
12 | :width: 600
13 | :align: center
14 |
15 | 玩家正在游戏中操控分身球。
16 |
17 | 为了更方便的介绍游戏规则,我们首先介绍 GoBigger 中出现的各种球。
18 |
19 | 游戏中的球
20 | ===============
21 | * 食物球
22 |
23 | 食物球是游戏中的中立资源。他们不属于任何玩家。他们会在游戏中源源不断地被补充进来,并且不会改变位置直到被吃掉为止。如果某个玩家操控的分身球吃掉了一个食物球,那么食物球的质量将会被传递到了分身球中。地图中的食物球数量会存在上限。
24 |
25 | .. only:: html
26 |
27 | .. figure:: images/eat_food.gif
28 | :width: 300
29 | :align: center
30 |
31 | 玩家操控的分身球吃掉了食物球。
32 |
33 |
34 | * 荆棘球
35 |
36 | 荆棘球也是游戏中的中立资源。与食物球不同在于,荆棘球一般拥有更大的尺寸,同时地图中的荆棘球数量上限会比食物球少很多。如果一个玩家的分身球吃掉了荆棘球(前提是分身球比荆棘球大),荆棘球的质量会被传递到分身球内,同时分身球会被引爆并分裂成多个小的分身球。在游戏中,分裂产生的小分身球会呈放射状发射出去,并在短时间内速度衰减。因此,虽然吃掉荆棘球是一个很好的发育方式,但是带来的分裂效果会使得玩家的分身球处在一个危险的境地中(可能会被其他玩家吃掉)。
37 |
38 | 荆棘球的另一个特点是可以被玩家通过吐孢子的方式移动。如果某个玩家的分身球对着荆棘球进行吐孢子,那么荆棘球将会吃掉这个孢子,获得质量,并朝着孢子的移动方向前进一小段距离。因此,玩家可以通过移动荆棘球来让更大的玩家的分身球引爆,从而寻找机会吃掉分裂出来的小球。
39 |
40 | .. only:: html
41 |
42 | .. figure:: images/on_thorns.gif
43 | :width: 300
44 | :align: center
45 |
46 | 玩家操控的分身球吃掉了荆棘球并被引爆。
47 |
48 | * 孢子球
49 |
50 | 孢子球是由玩家的分身球通过吐孢子的技能产生的。他们会获得一个初速度并移动一小段距离,然后静止在原地。孢子球可以被比他大的玩家和荆棘球吃掉。
51 |
52 | .. only:: html
53 |
54 | .. figure:: images/eject.gif
55 | :width: 300
56 | :align: center
57 |
58 | 玩家操控的分身球正在吐孢子。
59 |
60 | * 玩家的分身球
61 |
62 | 玩家的分身球就是玩家在游戏中操控的球。玩家可以对它的运动方向进行任意的改变,还可以吃掉比它小的其他球。在吃掉其他球的瞬间,玩家的分身球会获取到被吃球的质量,并且半径变大。为了增强游戏的可操作性,每个玩家的分身球都会有以下两种技能:
63 |
64 | .. only:: html
65 |
66 | .. figure:: images/eat_player.gif
67 | :width: 300
68 | :align: center
69 |
70 | 一个玩家正在吃掉另一个玩家的分身球。
71 |
72 | * 吐孢子
73 |
74 | 吐孢子可以帮助玩家的分身球快速减少体积。体积越小,移动速度的上限将会越高。当某个分身球吐孢子时,孢子会有一个初速度,并在一定时间内速度衰减为零。
75 |
76 | .. only:: html
77 |
78 | .. figure:: images/eject_to_thorns.gif
79 | :width: 300
80 | :align: center
81 |
82 | 玩家朝着荆棘球吐孢子。
83 |
84 | * 分裂
85 |
86 | 分裂技能可以帮助玩家分裂成相同大小的两部分。分裂之后,由于单个分身球的体积变小,对应的移动速度将会提高。分裂也是有代价的。请注意,在分裂之后,玩家的分身球会进入冷却期。在冷却期期间,分身球无法被自身的其他分身球合并。
87 |
88 | .. only:: html
89 |
90 | .. figure:: images/split.gif
91 | :width: 300
92 | :align: center
93 |
94 | 玩家正在进行分裂。
95 |
96 |
97 | 游戏规则
98 | ===============
99 |
100 | 如下,有一些值得注意的规则:
101 |
102 | 1. 玩家的分身球的质量会不断减少。GoBigger 设置了一个衰减系数,玩家球每秒的实际衰减系数会在默认衰减系数的基础上乘以球本身的半径。因此衰减在玩家球质量非常大的时候体现地更为明显。
103 |
104 | 2. 如果玩家的所有球都被吃掉了,那么他会立即随机在地图中重生。
105 |
106 | 3. 玩家的视野大小是由玩家的分身球位置所决定的。我们计算玩家所有分身球的质心,并获取到它的最小外接矩形,并在此基础上进行放大来决定该玩家的视野范围。同时,我们也会指定玩家的最小视野范围。玩家的分身球的相对距离越远,所能看到的视野范围将会越大。
107 |
108 | 4. 玩家的每个分身球都会根据其半径大小存在一个速度上限。在游戏中,半径越大,移动速度将会越慢。
109 |
110 |
111 | 高级操作
112 | ==============================
113 |
114 | .. only:: html
115 |
116 | .. figure:: images/merge_quickly.gif
117 | :width: 320
118 | :align: center
119 |
120 | 朝着中心吐孢子。
121 |
122 | .. only:: html
123 |
124 | .. figure:: images/split_eat_all.gif
125 | :width: 320
126 | :align: center
127 |
128 | 通过分裂快速吃掉其他玩家。
129 |
130 | .. only:: html
131 |
132 | .. figure:: images/fast_eat.gif
133 | :width: 320
134 | :align: center
135 |
136 | 快速吃掉食物球。
137 |
138 | .. only:: html
139 |
140 | .. figure:: images/eject_merger.gif
141 | :width: 422
142 | :align: center
143 |
144 | 聚集质量。
145 |
--------------------------------------------------------------------------------
/gobigger/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/gobigger/__init__.py
--------------------------------------------------------------------------------
/gobigger/agents/__init__.py:
--------------------------------------------------------------------------------
1 | from .base_agent import BaseAgent
2 | from .bot_agent import BotAgent
3 |
--------------------------------------------------------------------------------
/gobigger/agents/base_agent.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 |
4 |
5 | class BaseAgent:
6 | '''
7 | Overview:
8 | The base class of all agents
9 | '''
10 | def __init__(self):
11 | pass
12 |
13 | def step(self, obs):
14 | raise NotImplementedError
15 |
16 |
--------------------------------------------------------------------------------
/gobigger/agents/tests/test_base_agent.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 | import time
6 | import random
7 | import numpy as np
8 | import cv2
9 | import pygame
10 |
11 | from gobigger.agents import BaseAgent
12 | from gobigger.utils import Border
13 | from gobigger.server import Server
14 | from gobigger.render import RealtimeRender, RealtimePartialRender, EnvRender
15 |
16 | logging.basicConfig(level=logging.DEBUG)
17 |
18 |
19 | @pytest.mark.unittest
20 | class TestBaseAgent:
21 |
22 | def test_init(self):
23 | base_agent = BaseAgent()
24 | assert True
25 |
26 | def test_step(self):
27 | base_agent = BaseAgent()
28 | with pytest.raises(Exception) as e:
29 | base_agent.step(obs=None)
30 |
31 |
32 |
--------------------------------------------------------------------------------
/gobigger/agents/tests/test_bot_agent.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 | import time
6 | import random
7 | import numpy as np
8 | import cv2
9 | import pygame
10 |
11 | from gobigger.agents import BotAgent
12 | from gobigger.utils import Border
13 | from gobigger.server import Server
14 | from gobigger.render import RealtimeRender, RealtimePartialRender, EnvRender
15 |
16 | logging.basicConfig(level=logging.DEBUG)
17 |
18 |
19 | @pytest.mark.unittest
20 | class TestBotAgent:
21 |
22 | def test_step(self):
23 | server = Server(dict(
24 | team_num=4,
25 | player_num_per_team=3,
26 | frame_limit=20,
27 | ))
28 | server.reset()
29 | bot_agents = []
30 | for index, player in enumerate(server.player_manager.get_players()):
31 | bot_agents.append(BotAgent(player.player_id, level=index%3+1))
32 | logging.debug('players init: {}'.format(player.player_id))
33 | time_obs = 0
34 | time_step = 0
35 | time_fill_all = 0
36 | time_get_rectangle = 0
37 | time_get_clip = 0
38 | time_cvt = 0
39 | time_overlap = 0
40 | for i in range(100):
41 | t1 = time.time()
42 | obs = server.obs()
43 | t2 = time.time()
44 | if i % 4 == 0:
45 | actions = {bot_agent.name: bot_agent.step(obs[1][bot_agent.name]) for bot_agent in bot_agents}
46 | else:
47 | actions = None
48 | t3 = time.time()
49 | finish_flag = server.step(actions=actions)
50 | t4 = time.time()
51 | tmp_obs = t2-t1
52 | tmp_step = t4-t3
53 | time_obs += tmp_obs
54 | time_step += tmp_step
55 | logging.debug('{} {} obs: {:.3f} / {:.3f}, step: {:.3f} / {:.3f}'\
56 | .format(i, server.last_frame_count, tmp_obs, time_obs/(i+1), tmp_step, time_step/(i+1)))
57 |
58 | if finish_flag:
59 | logging.debug('Game Over')
60 | break
61 | server.close()
62 |
--------------------------------------------------------------------------------
/gobigger/balls/__init__.py:
--------------------------------------------------------------------------------
1 | from .base_ball import BaseBall
2 | from .food_ball import FoodBall
3 | from .spore_ball import SporeBall
4 | from .thorns_ball import ThornsBall
5 | from .clone_ball import CloneBall
--------------------------------------------------------------------------------
/gobigger/balls/food_ball.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from easydict import EasyDict
3 |
4 | from gobigger.utils import format_vector, add_score, Border, deep_merge_dicts
5 | from .base_ball import BaseBall
6 |
7 |
8 | class FoodBall(BaseBall):
9 | """
10 | Overview:
11 | - characteristic:
12 | * Can't move, can only be eaten, randomly generated
13 | """
14 | @staticmethod
15 | def default_config():
16 | cfg = BaseBall.default_config()
17 | cfg.update(dict(
18 | score_min=0.5,
19 | score_max=0.5,
20 | ))
21 | return EasyDict(cfg)
22 |
23 | def __init__(self, ball_id, position, score, border, **kwargs):
24 | super(FoodBall, self).__init__(ball_id, position, score=score, border=border, **kwargs)
25 | self.check_border()
26 |
27 | def move(self, direction, duration):
28 | logging.debug('FoodBall can not move')
29 | return
30 |
31 | def eat(self, ball):
32 | logging.debug('FoodBall can not eat others')
33 | return
34 |
35 | def save(self):
36 | return [self.position.x, self.position.y, self.radius]
37 |
--------------------------------------------------------------------------------
/gobigger/balls/spore_ball.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from easydict import EasyDict
3 | from pygame.math import Vector2
4 | import math
5 |
6 | from gobigger.utils import format_vector, add_score, Border, deep_merge_dicts
7 | from .base_ball import BaseBall
8 |
9 |
10 | class SporeBall(BaseBall):
11 | """
12 | Overview:
13 | Spores spit out by the player ball
14 | - characteristic:
15 | * Can't move actively
16 | * can not eat
17 | * Can be eaten by CloneBall and ThornsBall
18 | * There is an initial velocity at birth, and it decays to 0 within a period of time
19 | """
20 | @staticmethod
21 | def default_config():
22 | cfg = BaseBall.default_config()
23 | cfg.update(dict(
24 | score_init=1.5,
25 | vel_init=50,
26 | vel_zero_frame=10,
27 | ))
28 | return EasyDict(cfg)
29 |
30 | def __init__(self, ball_id, position, border, score, direction=Vector2(0,0), owner=-1, **kwargs):
31 | # init other kwargs
32 | kwargs = EasyDict(kwargs)
33 | cfg = SporeBall.default_config()
34 | cfg = deep_merge_dicts(cfg, kwargs)
35 | super(SporeBall, self).__init__(ball_id, position, score=score, border=border, **cfg)
36 | self.score_init = cfg.score_init
37 | self.vel_init = cfg.vel_init
38 | self.vel_zero_frame = cfg.vel_zero_frame
39 | # normal kwargs
40 | self.direction = direction.normalize()
41 | self.vel = self.vel_init * self.direction
42 | self.vel_piece = self.vel / self.vel_zero_frame
43 | self.owner = owner
44 | self.move_frame = 0
45 | # reset score
46 | if self.score != self.score_init:
47 | self.set_score(self.score_init)
48 | self.moving = True
49 | self.check_border()
50 |
51 | def move(self, direction=None, duration=0.05):
52 | assert direction is None
53 | assert duration > 0
54 | if self.moving:
55 | self.position = self.position + self.vel * duration
56 | self.move_frame += 1
57 | if self.move_frame < self.vel_zero_frame:
58 | self.vel -= self.vel_piece
59 | else:
60 | self.vel = Vector2(0, 0)
61 | self.vel_piece = Vector2(0, 0)
62 | self.moving = False
63 | self.check_border()
64 | return True
65 |
66 | def eat(self, ball):
67 | logging.debug('SporeBall can not eat others')
68 | return
69 |
70 | def save(self):
71 | return [self.position.x, self.position.y, self.radius]
72 |
--------------------------------------------------------------------------------
/gobigger/balls/tests/test_base_ball.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.balls import BaseBall
7 | from gobigger.utils import Border
8 |
9 | logging.basicConfig(level=logging.DEBUG)
10 |
11 | @pytest.mark.unittest
12 | class TestBaseBall:
13 |
14 | def test_init(self):
15 | border = Border(0, 0, 100, 100)
16 | position = Vector2(10, 10)
17 | ball_id = uuid.uuid1()
18 | base_ball = BaseBall(ball_id, position, border=border, score=6)
19 | assert True
20 |
21 | def test_judge_in_rectangle(self):
22 | border = Border(0, 0, 800, 800)
23 | position = Vector2(400, 400)
24 | ball_id = uuid.uuid1()
25 | base_ball = BaseBall(ball_id, position, border=border, score=6)
26 | rectangle = [300, 300, 500, 500]
27 | assert base_ball.judge_in_rectangle(rectangle)
28 |
29 | def test_move(self):
30 | border = Border(0, 0, 800, 800)
31 | position = Vector2(400, 400)
32 | ball_id = uuid.uuid1()
33 | base_ball = BaseBall(ball_id, position, border=border, score=6)
34 | with pytest.raises(Exception) as e:
35 | base_ball.move(direction=None, duration=None)
36 |
37 | def test_eat(self):
38 | border = Border(0, 0, 800, 800)
39 | position = Vector2(400, 400)
40 | ball_id = uuid.uuid1()
41 | base_ball = BaseBall(ball_id, position, border=border, score=6)
42 | with pytest.raises(Exception) as e:
43 | base_ball.eat(ball=None)
44 |
45 | def test_op_override(self):
46 | border = Border(0, 0, 800, 800)
47 | base_ball_1 = BaseBall(uuid.uuid1(), border.sample(), border=border, score=6)
48 | base_ball_2 = BaseBall(uuid.uuid1(), border.sample(), border=border, score=7)
49 | assert not base_ball_1 == base_ball_2
50 | assert base_ball_1 < base_ball_2
51 | assert not base_ball_1 > base_ball_2
52 |
--------------------------------------------------------------------------------
/gobigger/balls/tests/test_food_ball.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 | from easydict import EasyDict
6 |
7 | from gobigger.balls import FoodBall
8 | from gobigger.utils import Border
9 |
10 | logging.basicConfig(level=logging.DEBUG)
11 |
12 | @pytest.mark.unittest
13 | class TestFoodBall:
14 |
15 | def test_naive(self):
16 | ball_id = uuid.uuid1()
17 | border = Border(0, 0, 100, 100)
18 | position = Vector2(10, 10)
19 | food_ball = FoodBall(ball_id, position, border=border, score=1)
20 | assert True
21 |
22 | def test_default_config(self):
23 | assert isinstance(FoodBall.default_config(), EasyDict)
24 |
25 | def test_move(self):
26 | ball_id = uuid.uuid1()
27 | border = Border(0, 0, 100, 100)
28 | position = Vector2(10, 10)
29 | food_ball = FoodBall(ball_id, position, border=border, score=1)
30 | food_ball.move(direction=None, duration=None)
31 |
32 | def test_eat(self):
33 | ball_id = uuid.uuid1()
34 | border = Border(0, 0, 100, 100)
35 | position = Vector2(10, 10)
36 | food_ball = FoodBall(ball_id, position, border=border, score=1)
37 | food_ball.eat(ball=None)
--------------------------------------------------------------------------------
/gobigger/balls/tests/test_spore_ball.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.balls import SporeBall
7 | from gobigger.utils import Border
8 |
9 | logging.basicConfig(level=logging.DEBUG)
10 |
11 | @pytest.mark.unittest
12 | class TestSporesBall:
13 |
14 | def test_move(self):
15 | ball_id = uuid.uuid1()
16 | border = Border(0, 0, 1000, 1000)
17 | position = Vector2(100, 100)
18 | direction = Vector2(1, 0)
19 | spore_ball = SporeBall(ball_id, position, border=border, score=2, direction=direction)
20 | logging.debug('direction={}, position={}, vel={}, move_frame={}'
21 | .format(spore_ball.direction, spore_ball.position, spore_ball.vel, spore_ball.move_frame))
22 | for i in range(10):
23 | spore_ball.move(duration=0.05)
24 | logging.debug('[{}] direction={}, position={}, vel={}, move_frame={}'
25 | .format(i, spore_ball.direction, spore_ball.position, spore_ball.vel, spore_ball.move_frame))
26 | assert True
27 |
28 | def test_eat(self):
29 | ball_id = uuid.uuid1()
30 | border = Border(0, 0, 1000, 1000)
31 | position = Vector2(100, 100)
32 | direction = Vector2(1, 0)
33 | spore_ball = SporeBall(ball_id, position, border=border, score=2, direction=direction)
34 | spore_ball.eat(ball=None)
--------------------------------------------------------------------------------
/gobigger/balls/tests/test_thorns_ball.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.balls import BaseBall, ThornsBall, SporeBall
7 | from gobigger.utils import Border
8 |
9 | logging.basicConfig(level=logging.DEBUG)
10 |
11 | @pytest.mark.unittest
12 | class TestThornsBall:
13 |
14 | def test_init(self):
15 | ball_id = uuid.uuid1()
16 | border = Border(0, 0, 100, 100)
17 | position = Vector2(10, 10)
18 | thorns_ball = ThornsBall(ball_id, position, border=border, score=4)
19 | assert True
20 |
21 | def test_eat_move(self):
22 | ball_id = uuid.uuid1()
23 | border = Border(0, 0, 1000, 1000)
24 | thorns_position = Vector2(100, 100)
25 | thorns_score = ThornsBall.default_config().score_min
26 | thorns_ball = ThornsBall(ball_id, thorns_position, border=border, score=thorns_score)
27 |
28 | ball_id = uuid.uuid1()
29 | spore_position = Vector2(100, 100)
30 | spore_score = SporeBall.default_config().score_init
31 | direction = Vector2(1, 0)
32 | spore_ball = SporeBall(ball_id, spore_position, border=border, score=spore_score, direction=direction)
33 |
34 | logging.debug('=========================== before eat =============================')
35 | logging.debug('[thorns] position={}, score={}, vel={}, move_frame={}'
36 | .format(thorns_ball.position, thorns_ball.score, thorns_ball.vel, thorns_ball.move_frame))
37 | logging.debug('[spore] position={}, score={}, vel={}'
38 | .format(spore_ball.position, spore_ball.score, spore_ball.vel))
39 | thorns_ball.eat(spore_ball)
40 | logging.debug('=========================== after eat =============================')
41 | logging.debug('[thorns] position={}, score={}, vel={}, move_frame={}'
42 | .format(thorns_ball.position, thorns_ball.score, thorns_ball.vel, thorns_ball.move_frame))
43 | logging.debug('[spore] position={}, score={}, vel={}'
44 | .format(spore_ball.position, spore_ball.score, spore_ball.vel))
45 |
46 | for i in range(10):
47 | thorns_ball.move(duration=0.05)
48 | logging.debug('=========================== after move {} ============================='.format(i))
49 | logging.debug('[thorns] position={}, score={}, vel={}, move_frame={}'
50 | .format(thorns_ball.position, thorns_ball.score, thorns_ball.vel, thorns_ball.move_frame))
51 |
52 | assert True
53 |
54 | def test_judge_in_rectangle(self):
55 | border = Border(0, 0, 800, 800)
56 | position = Vector2(400, 400)
57 | ball_id = uuid.uuid1()
58 | thorns_ball = ThornsBall(ball_id, position, border=border, score=10)
59 | rectangle = [300, 300, 500, 500]
60 | assert thorns_ball.judge_in_rectangle(rectangle)
61 |
62 | def test_eat_others(self):
63 | border = Border(0, 0, 800, 800)
64 | position = Vector2(400, 400)
65 | ball_id = uuid.uuid1()
66 | thorns_ball = ThornsBall(ball_id, position, border=border, score=10)
67 | position = Vector2(10, 10)
68 | ball_id = uuid.uuid1()
69 | base_ball = BaseBall(ball_id, position, border=border, score=1)
70 | thorns_ball.eat(base_ball)
71 |
72 | ball_id = uuid.uuid1()
73 | spore_position = Vector2(100, 100)
74 | spore_score = SporeBall.default_config().score_init
75 | direction = Vector2(1, 0)
76 | spore_ball = SporeBall(ball_id, spore_position, border=border, score=spore_score, direction=direction)
77 | thorns_ball.set_score(thorns_ball.score_max)
78 | thorns_ball.eat(spore_ball)
79 |
--------------------------------------------------------------------------------
/gobigger/balls/thorns_ball.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from easydict import EasyDict
3 | import math
4 | from pygame.math import Vector2
5 |
6 | from gobigger.utils import format_vector, add_score, Border, deep_merge_dicts
7 | from .base_ball import BaseBall
8 | from .spore_ball import SporeBall
9 |
10 |
11 | class ThornsBall(BaseBall):
12 | """
13 | Overview:
14 | - characteristic:
15 | * Can't move actively
16 | * Can eat spores. When eating spores, it will inherit the momentum of the spores and move a certain distance.
17 | * Can only be eaten by balls heavier than him. After eating, it will split the host into multiple smaller units.
18 | * Nothing happens when a ball lighter than him passes by
19 | """
20 | @staticmethod
21 | def default_config():
22 | cfg = BaseBall.default_config()
23 | cfg.update(dict(
24 | score_min=3, # Minimum score, greater than the player's maximum number of clones multiplied by the player's minimum weight
25 | score_max=5, # Maximum score
26 | eat_spore_vel_init=4, # Initialization speed after eating spores
27 | eat_spore_vel_zero_frame=10, # Time to zero speed after eating spores(s)
28 | ))
29 | return EasyDict(cfg)
30 |
31 | def __init__(self, ball_id, position, score, border, **kwargs):
32 | # init other kwargs
33 | kwargs = EasyDict(kwargs)
34 | cfg = ThornsBall.default_config()
35 | cfg = deep_merge_dicts(cfg, kwargs)
36 | super(ThornsBall, self).__init__(ball_id, position, score=score, border=border, **cfg)
37 | self.score_min = cfg.score_min
38 | self.score_max = cfg.score_max
39 | self.eat_spore_vel_init = cfg.eat_spore_vel_init
40 | self.eat_spore_vel_zero_frame = cfg.eat_spore_vel_zero_frame
41 | self.move_frame = 0
42 | self.vel = Vector2(0, 0)
43 | self.vel_piece = Vector2(0, 0)
44 | self.moving = False
45 | self.check_border()
46 |
47 | def move(self, direction=None, duration=0.05, **kwargs):
48 | assert duration > 0
49 | if self.moving:
50 | self.position = self.position + self.vel * duration
51 | self.move_frame += 1
52 | if self.move_frame < self.eat_spore_vel_zero_frame:
53 | self.vel = self.vel - self.vel_piece
54 | else:
55 | self.vel = Vector2(0, 0)
56 | self.vel_piece = Vector2(0, 0)
57 | self.moving = False
58 | self.check_border()
59 | return True
60 |
61 | def eat(self, ball):
62 | if isinstance(ball, SporeBall):
63 | self.set_score(add_score(self.score, ball.score))
64 | if ball.vel.length() > 0:
65 | self.vel = self.eat_spore_vel_init * ball.vel.normalize()
66 | self.vel_piece = self.vel / self.eat_spore_vel_zero_frame
67 | self.move_time = 0
68 | self.moving = True
69 | else:
70 | logging.debug('ThornsBall can not eat {}'.format(type(ball)))
71 | return True
72 |
73 | def set_score(self, score: float) -> None:
74 | self.score = score
75 | if self.score > self.score_max:
76 | self.score = self.score_max
77 | elif self.score < self.score_min:
78 | self.score = self.score_min
79 | self.radius = self.score_to_radius(self.score)
80 |
81 | def save(self):
82 | return [self.position.x, self.position.y, self.radius]
83 |
--------------------------------------------------------------------------------
/gobigger/bin/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opendilab/GoBigger/630a7032976fc2c6ecbf87db2c77bb9665e219d4/gobigger/bin/__init__.py
--------------------------------------------------------------------------------
/gobigger/bin/demo_bot.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 | import time
6 | import numpy as np
7 | import cv2
8 | import pygame
9 | import pickle
10 |
11 | from gobigger.agents import BotAgent
12 | from gobigger.utils import Border
13 | from gobigger.server import Server
14 | from gobigger.render import RealtimeRender, RealtimePartialRender, EnvRender
15 | from gobigger.envs import GoBiggerEnv, create_env
16 |
17 | logging.basicConfig(level=logging.DEBUG)
18 |
19 |
20 | def demo_bot():
21 | env = GoBiggerEnv(dict(
22 | team_num=4,
23 | player_num_per_team=3,
24 | frame_limit=60*20*1,
25 | playback_settings=dict(
26 | save_video=True,
27 | save_all=True,
28 | save_partial=True,
29 | ),
30 | ))
31 | obs = env.reset()
32 | bot_agents = []
33 | team_infos = env.get_team_infos()
34 | for team_id, player_ids in team_infos:
35 | for player_id in player_ids:
36 | bot_agents.append(BotAgent(player_id, level=2))
37 | time_step_all = 0
38 | for i in range(100000):
39 | actions = {bot_agent.name: bot_agent.step(obs[1][bot_agent.name]) for bot_agent in bot_agents}
40 | t1 = time.time()
41 | obs, reward, done, info = env.step(actions=actions)
42 | t2 = time.time()
43 | time_step_all += t2-t1
44 | logging.debug('{} {:.4f} envstep {:.3f} / {:.3f}, leaderboard={}'\
45 | .format(i, obs[0]['last_frame_count'], t2-t1, time_step_all/(i+1), obs[0]['leaderboard']))
46 | if done:
47 | logging.debug('Game Over')
48 | break
49 | env.close()
50 |
51 |
52 | def demo_bot_st_t2p2():
53 | env = create_env('st_t3p2', dict(
54 | playback_settings=dict(
55 | playback_type='by_frame',
56 | by_frame=dict(
57 | save_frame=True,
58 | ),
59 | ),
60 | ), step_mul=10)
61 | obs = env.reset()
62 | bot_agents = []
63 | team_infos = env.get_team_infos()
64 | print(team_infos)
65 | for team_id, player_ids in team_infos:
66 | for player_id in player_ids:
67 | bot_agents.append(BotAgent(player_id, level=2))
68 | time_step_all = 0
69 | for i in range(100000):
70 | actions = {bot_agent.name: bot_agent.step(obs[1][bot_agent.name]) for bot_agent in bot_agents}
71 | t1 = time.time()
72 | obs, reward, done, info = env.step(actions=actions)
73 | t2 = time.time()
74 | time_step_all += t2-t1
75 | logging.debug('{} {:.4f} envstep {:.3f} / {:.3f}, leaderboard={}'\
76 | .format(i, obs[0]['last_frame_count'], t2-t1, time_step_all/(i+1), obs[0]['leaderboard']))
77 | if done:
78 | logging.debug('Game Over')
79 | break
80 | env.close()
81 |
82 | if __name__ == '__main__':
83 | # demo_bot()
84 | demo_bot_st_t2p2()
85 |
--------------------------------------------------------------------------------
/gobigger/bin/play_hyper.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 | import pygame
6 | import numpy as np
7 | import cv2
8 | import argparse
9 | import time
10 | import importlib
11 |
12 | from gobigger.utils import Border
13 | from gobigger.server import Server
14 | from gobigger.render import RealtimeRender, RealtimePartialRender, EnvRender
15 | from gobigger.agents import BotAgent
16 |
17 | logging.basicConfig(level=logging.DEBUG)
18 |
19 |
20 | def play_by_config(config_name):
21 | config_module = importlib.import_module('gobigger.hyper.configs.config_{}'.format(config_name))
22 | config = config_module.server_default_config
23 | server = Server(config)
24 | server.reset()
25 | render = RealtimeRender(server.map_width, server.map_height)
26 | server.set_render(render)
27 | human_team_name = '0'
28 | human_team_player_name = []
29 | bot_agents = []
30 | for player in server.player_manager.get_players():
31 | if player.team_name != human_team_name:
32 | bot_agents.append(BotAgent(player.name))
33 | else:
34 | human_team_player_name.append(player.name)
35 | fps_real = 0
36 | t1 = time.time()
37 | clock = pygame.time.Clock()
38 | fps_set = server.state_tick_per_second
39 | for i in range(100000):
40 | obs = server.obs()
41 | # actions_bot = {bot_agent.name: bot_agent.step(obs[1][bot_agent.name]) for bot_agent in bot_agents}
42 | actions_bot = {bot_agent.name: [None, None, -1] for bot_agent in bot_agents}
43 | actions = {player_name: [None, None, -1] for player_name in human_team_player_name}
44 | x, y = None, None
45 | action_type = -1
46 | # ================ control by keyboard ===============
47 | for event in pygame.event.get():
48 | if event.type == pygame.KEYDOWN:
49 | x1, y1, x2, y2 = None, None, None, None
50 | action_type1 = -1
51 | action_type2 = -1
52 | if event.key == pygame.K_UP:
53 | x1, y1 = 0, -1
54 | if event.key == pygame.K_DOWN:
55 | x1, y1 = 0, 1
56 | if event.key == pygame.K_LEFT:
57 | x1, y1 = -1, 0
58 | if event.key == pygame.K_RIGHT:
59 | x1, y1 = 1, 0
60 | if event.key == pygame.K_LEFTBRACKET: # Spores
61 | action_type1 = 0
62 | if event.key == pygame.K_RIGHTBRACKET: # Splite
63 | action_type1 = 1
64 | if event.key == pygame.K_BACKSLASH: # Stop moving
65 | action_type1 = 2
66 | if event.key == pygame.K_w:
67 | x2, y2 = 0, -1
68 | if event.key == pygame.K_s:
69 | x2, y2 = 0, 1
70 | if event.key == pygame.K_a:
71 | x2, y2 = -1, 0
72 | if event.key == pygame.K_d:
73 | x2, y2 = 1, 0
74 | if event.key == pygame.K_1: # Spores
75 | action_type2 = 0
76 | if event.key == pygame.K_2: # Splite
77 | action_type2 = 1
78 | if event.key == pygame.K_3: # Stop moving
79 | action_type2 = 2
80 | actions = {
81 | human_team_player_name[0]: [x1, y1, action_type1],
82 | human_team_player_name[1]: [x2, y2, action_type2],
83 | }
84 | if server.last_time < server.match_time:
85 | actions.update(actions_bot)
86 | print(actions)
87 | server.step_state_tick(actions=actions)
88 | if actions is not None and x is not None and y is not None:
89 | render.fill(server, direction=Vector2(x, y), fps=fps_real, last_time=server.last_time)
90 | else:
91 | render.fill(server, direction=None, fps=fps_real, last_time=server.last_time)
92 | render.show()
93 | if i % server.state_tick_per_second == 0:
94 | t2 = time.time()
95 | fps_real = server.state_tick_per_second/(t2-t1)
96 | t1 = time.time()
97 | else:
98 | logging.debug('Game Over')
99 | break
100 | clock.tick(fps_set)
101 | render.close()
102 |
103 |
104 | if __name__ == '__main__':
105 | parser = argparse.ArgumentParser()
106 | parser.add_argument('-c', '--config', type=str, default='2f2s')
107 | args = parser.parse_args()
108 |
109 | play_by_config(args.config)
110 |
--------------------------------------------------------------------------------
/gobigger/bin/profile.py:
--------------------------------------------------------------------------------
1 | import time
2 | from pygame.math import Vector2
3 | import numpy as np
4 | import numexpr as ne
5 | import random
6 |
7 | from pygame.math import Vector2
8 |
9 | from gobigger.agents import BotAgent
10 | from gobigger.utils import Border
11 | from gobigger.server import Server
12 | from gobigger.render import RealtimeRender, RealtimePartialRender, EnvRender
13 |
14 |
15 | def method1(food_balls, cx, cy, r):
16 | food_count = 0
17 | food = len(food_balls) * [3 * [None]]
18 | food_radius = 2
19 | for ball in food_balls:
20 | x = ball.x
21 | y = ball.y
22 | if (x-cx)**2 + (y-cy)**2 < r**2:
23 | food[food_count] = [x, y, food_radius]
24 | food_count += 1
25 | return food[:food_count]
26 |
27 |
28 | def method2(food_balls, cx, cy, r):
29 | t1 = time.time()
30 | X = np.array([ball.x for ball in food_balls])
31 | Y = np.array([ball.y for ball in food_balls])
32 | t2 = time.time()
33 | res = ne.evaluate('((X-cx)**2 + (Y-cy)**2) eat_ratio * size_B
11 | playback_settings=dict(
12 | playback_type='none', # ['', 'by_video', 'by_frame', 'by_action']
13 | by_video=dict(
14 | save_video=False,
15 | save_fps=10,
16 | save_resolution=552,
17 | save_all=True,
18 | save_partial=False,
19 | save_dir='.',
20 | save_name_prefix='test',
21 | ),
22 | by_frame=dict(
23 | save_frame=False,
24 | save_all=True,
25 | save_partial=False,
26 | save_dir='.',
27 | save_name_prefix='test',
28 | ),
29 | by_action=dict(
30 | save_action=False,
31 | save_dir='.',
32 | save_name_prefix='test',
33 | ),
34 | ),
35 | opening_settings=dict(
36 | opening_type='none', # ['none', 'handcraft', 'from_frame']
37 | handcraft=dict(
38 | food=[], # only position and radius
39 | thorns=[], # only position and radius
40 | spore=[], # only position and radius
41 | clone=[], # only position and radius and player and team
42 | ),
43 | from_frame=dict(
44 | frame_path='',
45 | ),
46 | ),
47 | manager_settings=dict(
48 | # food setting
49 | food_manager=dict(
50 | num_init=800, # initial number
51 | num_min=800, # Minimum number
52 | num_max=900, # Maximum number
53 | refresh_frame_freq=8, # Time interval (seconds) for refreshing food in the map
54 | refresh_percent=0.01, # The number of refreshed foods in the map each time
55 | ball_settings=dict( # The specific parameter description can be viewed in the ball module
56 | score_min=100, # radius=0.438, # score = 100
57 | score_max=100,
58 | ),
59 | ),
60 | # thorns setting
61 | thorns_manager=dict(
62 | num_init=9, # initial number
63 | num_min=9, # Minimum number
64 | num_max=12, # Maximum number
65 | refresh_frame_freq=120, # Time interval (seconds) for refreshing thorns in the map
66 | refresh_percent=0.2, # The number of refreshed thorns in the map each time
67 | ball_settings=dict( # The specific parameter description can be viewed in the ball module
68 | score_min=10000, # 2.086
69 | score_max=15000, # 2.540
70 | eat_spore_vel_init=10,
71 | eat_spore_vel_zero_frame=20,
72 | ),
73 | ),
74 | # player setting
75 | player_manager=dict(
76 | ball_settings=dict( # The specific parameter description can be viewed in the ball module
77 | acc_weight=30,
78 | vel_max=50,
79 | score_init=1000, # 0.755,
80 | score_respawn=1000, # score after respawn, usually the same with score_init
81 | part_num_max=16,
82 | on_thorns_part_num=10,
83 | on_thorns_part_score_max=5000,
84 | split_score_min=3600, # radius = 1.289
85 | eject_score_min=3200, # radius = 1.222
86 | recombine_frame=400,
87 | split_vel_zero_frame=14,
88 | score_decay_min=2600,
89 | score_decay_rate_per_frame=0.00005, # * sqrt(radius)
90 | center_acc_weight=10,
91 | ),
92 | ),
93 | # spore setting
94 | spore_manager=dict(
95 | ball_settings=dict( # The specific parameter description can be viewed in the ball module
96 | score_init=1400, # 0.859
97 | vel_init=30,
98 | vel_zero_frame=20,
99 | ),
100 | )
101 | ),
102 | obs_settings=dict(
103 | obs_type='partial', # ['partial', 'all']
104 | partial=dict(
105 | type='square', # ['circle', 'square']
106 | vision_x_min=10,
107 | vision_y_min=10,
108 | scale_up_ratio=1.8,
109 | )
110 | ),
111 | )
112 |
--------------------------------------------------------------------------------
/gobigger/configs/server_sp_default_config.py:
--------------------------------------------------------------------------------
1 | from .server_default_config import server_default_config
2 |
3 | server_sp_default_config = server_default_config
4 |
--------------------------------------------------------------------------------
/gobigger/configs/sp_t4p3.py:
--------------------------------------------------------------------------------
1 | from .server_sp_default_config import server_sp_default_config
2 |
3 | sp_t4p3 = server_sp_default_config
4 |
--------------------------------------------------------------------------------
/gobigger/configs/st_t1p1.py:
--------------------------------------------------------------------------------
1 | from easydict import EasyDict
2 | from gobigger.utils import deep_merge_dicts
3 | from .server_default_config import server_default_config
4 |
5 | cfg_ori = EasyDict(server_default_config)
6 | st_t1p1 = EasyDict(dict(
7 | team_num=1,
8 | player_num_per_team=1,
9 | map_width=32,
10 | map_height=32,
11 | frame_limit=60*3*20,
12 | manager_settings=dict(
13 | food_manager=dict(
14 | num_init=65,
15 | num_min=65,
16 | num_max=75,
17 | ),
18 | thorns_manager=dict(
19 | num_init=1,
20 | num_min=1,
21 | num_max=2,
22 | ),
23 | player_manager=dict(
24 | ball_settings=dict(
25 | score_init=1000,
26 | ),
27 | ),
28 | ),
29 | ))
30 | st_t1p1 = deep_merge_dicts(cfg_ori, st_t1p1)
31 |
--------------------------------------------------------------------------------
/gobigger/configs/st_t1p2.py:
--------------------------------------------------------------------------------
1 | from easydict import EasyDict
2 | from gobigger.utils import deep_merge_dicts
3 | from .server_default_config import server_default_config
4 |
5 | cfg_ori = EasyDict(server_default_config)
6 | st_t1p2 = EasyDict(dict(
7 | team_num=1,
8 | player_num_per_team=2,
9 | map_width=48,
10 | map_height=48,
11 | frame_limit=60*3*20,
12 | manager_settings=dict(
13 | food_manager=dict(
14 | num_init=130,
15 | num_min=130,
16 | num_max=150,
17 | ),
18 | thorns_manager=dict(
19 | num_init=2,
20 | num_min=2,
21 | num_max=3,
22 | ),
23 | player_manager=dict(
24 | ball_settings=dict(
25 | score_init=1000,
26 | ),
27 | ),
28 | ),
29 | ))
30 | st_t1p2 = deep_merge_dicts(cfg_ori, st_t1p2)
31 |
--------------------------------------------------------------------------------
/gobigger/configs/st_t2p1.py:
--------------------------------------------------------------------------------
1 | from easydict import EasyDict
2 | from gobigger.utils import deep_merge_dicts
3 | from .server_default_config import server_default_config
4 |
5 | cfg_ori = EasyDict(server_default_config)
6 | st_t2p1 = EasyDict(dict(
7 | team_num=2,
8 | player_num_per_team=1,
9 | map_width=48,
10 | map_height=48,
11 | frame_limit=60*3*20,
12 | manager_settings=dict(
13 | food_manager=dict(
14 | num_init=130,
15 | num_min=130,
16 | num_max=150,
17 | ),
18 | thorns_manager=dict(
19 | num_init=2,
20 | num_min=2,
21 | num_max=3,
22 | ),
23 | player_manager=dict(
24 | ball_settings=dict(
25 | score_init=1000,
26 | ),
27 | ),
28 | ),
29 | ))
30 | st_t2p1 = deep_merge_dicts(cfg_ori, st_t2p1)
31 |
--------------------------------------------------------------------------------
/gobigger/configs/st_t2p2.py:
--------------------------------------------------------------------------------
1 | from easydict import EasyDict
2 | from gobigger.utils import deep_merge_dicts
3 | from .server_default_config import server_default_config
4 |
5 | cfg_ori = EasyDict(server_default_config)
6 | st_t2p2 = EasyDict(dict(
7 | team_num=2,
8 | player_num_per_team=2,
9 | map_width=64,
10 | map_height=64,
11 | frame_limit=60*3*20,
12 | manager_settings=dict(
13 | food_manager=dict(
14 | num_init=260,
15 | num_min=260,
16 | num_max=300,
17 | ),
18 | thorns_manager=dict(
19 | num_init=3,
20 | num_min=3,
21 | num_max=4,
22 | ),
23 | player_manager=dict(
24 | ball_settings=dict(
25 | score_init=13000,
26 | ),
27 | ),
28 | ),
29 | ))
30 | st_t2p2 = deep_merge_dicts(cfg_ori, st_t2p2)
31 |
--------------------------------------------------------------------------------
/gobigger/configs/st_t3p2.py:
--------------------------------------------------------------------------------
1 | from easydict import EasyDict
2 | from gobigger.utils import deep_merge_dicts
3 | from .server_default_config import server_default_config
4 |
5 | cfg_ori = EasyDict(server_default_config)
6 | st_t3p2 = EasyDict(dict(
7 | team_num=3,
8 | player_num_per_team=2,
9 | map_width=88,
10 | map_height=88,
11 | frame_limit=60*3*20,
12 | manager_settings=dict(
13 | food_manager=dict(
14 | num_init=500,
15 | num_min=500,
16 | num_max=560,
17 | ),
18 | thorns_manager=dict(
19 | num_init=5,
20 | num_min=5,
21 | num_max=6,
22 | ),
23 | player_manager=dict(
24 | ball_settings=dict(
25 | score_init=13000,
26 | ),
27 | ),
28 | ),
29 | ))
30 | st_t3p2 = deep_merge_dicts(cfg_ori, st_t3p2)
31 |
--------------------------------------------------------------------------------
/gobigger/configs/st_t4p3.py:
--------------------------------------------------------------------------------
1 | from .server_default_config import server_default_config
2 |
3 | st_t4p3 = server_default_config
4 |
--------------------------------------------------------------------------------
/gobigger/configs/st_t5p3.py:
--------------------------------------------------------------------------------
1 | from easydict import EasyDict
2 | from gobigger.utils import deep_merge_dicts
3 | from .server_default_config import server_default_config
4 |
5 | cfg_ori = EasyDict(server_default_config)
6 | st_t5p3 = EasyDict(dict(
7 | team_num=5,
8 | player_num_per_team=3,
9 | map_width=128,
10 | map_height=128,
11 | frame_limit=60*12*20,
12 | manager_settings=dict(
13 | food_manager=dict(
14 | num_init=900,
15 | num_min=900,
16 | num_max=1000,
17 | ),
18 | thorns_manager=dict(
19 | num_init=10,
20 | num_min=12,
21 | num_max=12,
22 | ),
23 | player_manager=dict(
24 | ball_settings=dict(
25 | score_init=1000,
26 | ),
27 | ),
28 | ),
29 | ))
30 | st_t5p3 = deep_merge_dicts(cfg_ori, st_t5p3)
31 |
--------------------------------------------------------------------------------
/gobigger/configs/st_t5p4.py:
--------------------------------------------------------------------------------
1 | from easydict import EasyDict
2 | from gobigger.utils import deep_merge_dicts
3 | from .server_default_config import server_default_config
4 |
5 | cfg_ori = EasyDict(server_default_config)
6 | st_t5p4 = EasyDict(dict(
7 | team_num=5,
8 | player_num_per_team=4,
9 | map_width=144,
10 | map_height=144,
11 | frame_limit=60*12*20,
12 | manager_settings=dict(
13 | food_manager=dict(
14 | num_init=900,
15 | num_min=900,
16 | num_max=1000,
17 | ),
18 | thorns_manager=dict(
19 | num_init=10,
20 | num_min=12,
21 | num_max=12,
22 | ),
23 | player_manager=dict(
24 | ball_settings=dict(
25 | score_init=1000,
26 | ),
27 | ),
28 | ),
29 | ))
30 | st_t5p4 = deep_merge_dicts(cfg_ori, st_t5p4)
31 |
--------------------------------------------------------------------------------
/gobigger/configs/st_t6p4.py:
--------------------------------------------------------------------------------
1 | from easydict import EasyDict
2 | from gobigger.utils import deep_merge_dicts
3 | from .server_default_config import server_default_config
4 |
5 | cfg_ori = EasyDict(server_default_config)
6 | st_t6p4 = EasyDict(dict(
7 | team_num=6,
8 | player_num_per_team=4,
9 | map_width=144,
10 | map_height=144,
11 | frame_limit=60*12*20,
12 | manager_settings=dict(
13 | food_manager=dict(
14 | num_init=1000,
15 | num_min=1000,
16 | num_max=1100,
17 | ),
18 | thorns_manager=dict(
19 | num_init=11,
20 | num_min=13,
21 | num_max=13,
22 | ),
23 | player_manager=dict(
24 | ball_settings=dict(
25 | score_init=1000,
26 | ),
27 | ),
28 | ),
29 | ))
30 | st_t6p4 = deep_merge_dicts(cfg_ori, st_t6p4)
31 |
--------------------------------------------------------------------------------
/gobigger/envs/__init__.py:
--------------------------------------------------------------------------------
1 | import importlib
2 |
3 | from .gobigger_env import GoBiggerEnv
4 | from .gobigger_sp_env import GoBiggerSPEnv
5 | from gobigger.configs import *
6 | from gobigger.utils import deep_merge_dicts
7 |
8 | def create_env_st(cfg, **kwargs):
9 | return GoBiggerEnv(cfg, **kwargs)
10 |
11 | def create_env_sp(cfg, **kwargs):
12 | return GoBiggerSPEnv(cfg, **kwargs)
13 |
14 | def create_env(env_name, custom_cfg={}, **kwargs):
15 | '''
16 | env_name choice in ['st_v0', 'sp_v0']
17 | '''
18 | cfg = importlib.import_module('gobigger.configs.{}'.format(env_name))
19 | cfg = eval('cfg.{}'.format(env_name))
20 | cfg = deep_merge_dicts(cfg, custom_cfg)
21 | if env_name.startswith('st'):
22 | return create_env_st(cfg, **kwargs)
23 | elif env_name.startswith('sp'):
24 | return create_env_sp(cfg, **kwargs)
25 | else:
26 | raise NotImplementedError
27 |
28 | def create_env_custom(type, cfg=None, **kwargs):
29 | if type == 'st':
30 | return create_env_st(cfg, **kwargs)
31 | elif type == 'sp':
32 | return create_env_sp(cfg, **kwargs)
33 | else:
34 | raise NotImplementedError
35 |
--------------------------------------------------------------------------------
/gobigger/envs/gobigger_env.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import gym
4 | import time
5 |
6 | from gobigger.server import Server
7 | from gobigger.render import EnvRender
8 | import copy
9 |
10 |
11 | class GoBiggerEnv(gym.Env):
12 |
13 | def __init__(self, server_cfg=None, step_mul=2, **kwargs):
14 | self.server_cfg = server_cfg
15 | self.step_mul = step_mul
16 | self.init_server()
17 |
18 | def step(self, actions):
19 | for i in range(self.step_mul):
20 | if i==0:
21 | done = self.server.step(actions=actions)
22 | else:
23 | done = self.server.step(actions=None)
24 | obs_raw = self.server.obs()
25 | global_state, player_states, info = obs_raw
26 | obs = [global_state, player_states]
27 | total_score = [global_state['leaderboard'][i] \
28 | for i in range(len(global_state['leaderboard']))]
29 | assert len(self.last_total_score) == len(total_score)
30 | reward = [total_score[i] - self.last_total_score[i] for i in range(len(total_score))]
31 | self.last_total_score = total_score
32 | return obs, reward, done, info
33 |
34 | def reset(self):
35 | self.server.reset()
36 | obs_raw = self.server.obs()
37 | global_state, player_states, info = obs_raw
38 | obs = [global_state, player_states]
39 | self.last_total_score = [global_state['leaderboard'][i] \
40 | for i in range(len(global_state['leaderboard']))]
41 | return obs
42 |
43 | def close(self):
44 | self.server.close()
45 |
46 | def seed(self, seed):
47 | self.server.seed(seed)
48 |
49 | def get_team_infos(self):
50 | assert hasattr(self, 'server'), "Please call `reset()` first"
51 | return self.server.get_team_infos()
52 |
53 | def init_server(self):
54 | self.server = Server(cfg=self.server_cfg)
55 |
--------------------------------------------------------------------------------
/gobigger/envs/gobigger_sp_env.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import gym
4 | import time
5 |
6 | from gobigger.server import ServerSP
7 | from gobigger.render import EnvRender
8 | from .gobigger_env import GoBiggerEnv
9 |
10 |
11 | class GoBiggerSPEnv(GoBiggerEnv):
12 |
13 | def init_server(self):
14 | self.server = ServerSP(cfg=self.server_cfg)
15 |
--------------------------------------------------------------------------------
/gobigger/envs/tests/test_gobigger_env.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.envs import GoBiggerEnv
7 |
8 | logging.basicConfig(level=logging.DEBUG)
9 |
10 |
11 | @pytest.mark.unittest
12 | class TestGoBiggerEnv:
13 |
14 | def test_env(self):
15 | env = GoBiggerEnv()
16 | obs = env.reset()
17 | env.seed(1000)
18 | obs, reward, done, info = env.step(actions=None)
19 | global_state, player_states = obs
20 | assert len(player_states) == env.server.team_num * env.server.player_num_per_team
21 | env.close()
22 | assert True
23 |
--------------------------------------------------------------------------------
/gobigger/envs/tests/test_gobigger_sp_env.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.envs import GoBiggerSPEnv
7 |
8 | logging.basicConfig(level=logging.DEBUG)
9 |
10 |
11 | @pytest.mark.unittest
12 | class TestGoBiggerEnv:
13 |
14 | def test_env(self):
15 | env = GoBiggerSPEnv()
16 | obs = env.reset()
17 | env.seed(1000)
18 | obs, reward, done, info = env.step(actions=None)
19 | global_state, player_states = obs
20 | assert len(player_states) == env.server.team_num * env.server.player_num_per_team
21 | env.close()
22 | assert True
23 |
--------------------------------------------------------------------------------
/gobigger/managers/__init__.py:
--------------------------------------------------------------------------------
1 | from .base_manager import BaseManager
2 | from .food_manager import FoodManager
3 | from .spore_manager import SporeManager
4 | from .thorns_manager import ThornsManager
5 | from .player_manager import PlayerManager
6 | from .player_sp_manager import PlayerSPManager
7 |
--------------------------------------------------------------------------------
/gobigger/managers/base_manager.py:
--------------------------------------------------------------------------------
1 | import math
2 | import logging
3 | from abc import ABC, abstractmethod
4 | from easydict import EasyDict
5 | from pygame.math import Vector2
6 |
7 | from gobigger.utils import format_vector, Border
8 | from gobigger.balls import FoodBall, ThornsBall, CloneBall, SporeBall
9 |
10 |
11 | class BaseManager(ABC):
12 | '''
13 | Overview:
14 | Base class for all ball managers
15 | '''
16 | def __init__(self, cfg, border):
17 | self.cfg = cfg
18 | self.border = border
19 | self.balls = {}
20 | self.ball_settings = self.cfg.ball_settings
21 |
22 | def get_balls(self):
23 | '''
24 | Overview:
25 | Get all balls currently managed
26 | '''
27 | return self.balls.values()
28 |
29 | def add_balls(self, balls):
30 | '''
31 | Overview:
32 | Add one (or more) balls
33 | '''
34 | raise NotImplementedError
35 |
36 | def refresh(self):
37 | '''
38 | Overview:
39 | Refresh. Used to refresh the balls in management. Such as replenishing eaten food balls
40 | '''
41 | raise NotImplementedError
42 |
43 | def remove_balls(self, balls):
44 | '''
45 | Overview:
46 | Remove managed balls
47 | '''
48 | raise NotImplementedError
49 |
50 | def spawn_ball(self):
51 | raise NotImplementedError
52 |
53 | def init_balls(self):
54 | raise NotImplementedError
55 |
56 | def step(self, duration):
57 | '''
58 | Overview:
59 | Perform a status update under the control of the server
60 | '''
61 | raise NotImplementedError
62 |
63 | def obs(self):
64 | '''
65 | Overview:
66 | Return data available for observation
67 | '''
68 | raise NotImplementedError
69 |
70 | def reset(self):
71 | raise NotImplementedError
72 |
--------------------------------------------------------------------------------
/gobigger/managers/food_manager.py:
--------------------------------------------------------------------------------
1 | import math
2 | import logging
3 | import random
4 | import uuid
5 | import numpy as np
6 | from abc import ABC, abstractmethod
7 | from easydict import EasyDict
8 | from pygame.math import Vector2
9 |
10 | from .base_manager import BaseManager
11 | from gobigger.utils import format_vector, Border, SequenceGenerator
12 | from gobigger.balls import FoodBall, ThornsBall, CloneBall, SporeBall
13 |
14 |
15 | class FoodManager(BaseManager):
16 |
17 | def __init__(self, cfg, border, random_generator=None, sequence_generator=None):
18 | super(FoodManager, self).__init__(cfg, border)
19 | self.refresh_frame_freq = self.cfg.refresh_frame_freq
20 | self.refresh_frame_count = 0
21 | if random_generator is not None:
22 | self._random = random_generator
23 | else:
24 | self._random = random.Random()
25 | if sequence_generator is not None:
26 | self.sequence_generator = sequence_generator
27 | else:
28 | self.sequence_generator = SequenceGenerator()
29 |
30 | def get_balls(self):
31 | return list(self.balls.values())
32 |
33 | def add_balls(self, balls):
34 | if isinstance(balls, list):
35 | for ball in balls:
36 | self.balls[ball.ball_id] = ball
37 | elif isinstance(balls, FoodBall):
38 | self.balls[balls.ball_id] = balls
39 | return True
40 |
41 | def refresh(self):
42 | left_num = self.cfg.num_max - len(self.balls)
43 | todo_num = min(math.ceil(self.cfg.refresh_percent * left_num), left_num)
44 | new_balls = {}
45 | for _ in range(todo_num):
46 | ball = self.spawn_ball()
47 | self.add_balls(ball)
48 | new_balls[ball.ball_id] = ball.save()
49 | return new_balls
50 |
51 | def remove_balls(self, balls):
52 | if isinstance(balls, list):
53 | for ball in balls:
54 | ball.remove()
55 | try:
56 | del self.balls[ball.ball_id]
57 | except:
58 | pass
59 | elif isinstance(balls, FoodBall):
60 | balls.remove()
61 | try:
62 | del self.balls[balls.ball_id]
63 | except:
64 | pass
65 |
66 | def spawn_ball(self, position=None, score=None):
67 | if position is None:
68 | position = self.border.sample()
69 | if score is None:
70 | score = self._random.uniform(self.ball_settings.score_min, self.ball_settings.score_max)
71 | ball_id = self.sequence_generator.get()
72 | return FoodBall(ball_id=ball_id, position=position, border=self.border, score=score, **self.ball_settings)
73 |
74 | def init_balls(self, custom_init=None):
75 | if custom_init is None or len(custom_init) == 0:
76 | for _ in range(self.cfg.num_init):
77 | ball = self.spawn_ball()
78 | self.balls[ball.ball_id] = ball
79 | else:
80 | for ball_cfg in custom_init:
81 | ball = self.spawn_ball(position=Vector2(*ball_cfg[:2]), score=ball_cfg[2])
82 | self.balls[ball.ball_id] = ball
83 |
84 | def step(self, duration):
85 | self.refresh_frame_count += 1
86 | new_balls = {}
87 | if self.refresh_frame_count >= self.refresh_frame_freq:
88 | new_balls = self.refresh()
89 | self.refresh_frame_count = 0
90 | return new_balls
91 |
92 | def reset(self):
93 | self.refresh_frame_count = 0
94 | self.balls = {}
95 | return True
96 |
--------------------------------------------------------------------------------
/gobigger/managers/player_sp_manager.py:
--------------------------------------------------------------------------------
1 | from gobigger.utils import SequenceGenerator
2 | from gobigger.players import HumanSPPlayer
3 | from .player_manager import PlayerManager
4 |
5 |
6 | class PlayerSPManager(PlayerManager):
7 |
8 | def __init__(self, cfg, border, team_num, player_num_per_team, spore_manager_settings,
9 | random_generator=None, sequence_generator=None):
10 | super(PlayerSPManager, self).__init__(cfg, border, team_num, player_num_per_team, spore_manager_settings,
11 | random_generator=random_generator)
12 | if sequence_generator is not None:
13 | self.sequence_generator = sequence_generator
14 | else:
15 | self.sequence_generator = SequenceGenerator()
16 |
17 | def init_balls(self, custom_init=None):
18 | if custom_init is None or len(custom_init) == 0:
19 | for i in range(self.team_num):
20 | team_id = i
21 | for j in range(self.player_num_per_team):
22 | player_id = i * self.player_num_per_team + j
23 | player = HumanSPPlayer(cfg=self.cfg.ball_settings, team_id=team_id, player_id=player_id,
24 | border=self.border, spore_settings=self.spore_settings,
25 | sequence_generator=self.sequence_generator)
26 | player.respawn(position=self.border.sample())
27 | self.players[player_id] = player
28 | else:
29 | raise NotImplementedError
30 |
--------------------------------------------------------------------------------
/gobigger/managers/spore_manager.py:
--------------------------------------------------------------------------------
1 | import math
2 | import logging
3 | import random
4 | import uuid
5 | from abc import ABC, abstractmethod
6 | from easydict import EasyDict
7 | from pygame.math import Vector2
8 |
9 | from .base_manager import BaseManager
10 | from gobigger.utils import format_vector, Border, SequenceGenerator
11 | from gobigger.balls import FoodBall, ThornsBall, CloneBall, SporeBall
12 |
13 |
14 | class SporeManager(BaseManager):
15 |
16 | def __init__(self, cfg, border, random_generator=None, sequence_generator=None):
17 | super(SporeManager, self).__init__(cfg, border)
18 | if random_generator is not None:
19 | self._random = random_generator
20 | else:
21 | self._random = random.Random()
22 | if sequence_generator is not None:
23 | self.sequence_generator = sequence_generator
24 | else:
25 | self.sequence_generator = SequenceGenerator()
26 |
27 | def get_balls(self):
28 | return list(self.balls.values())
29 |
30 | def add_balls(self, balls):
31 | if isinstance(balls, list):
32 | for ball in balls:
33 | self.balls[ball.ball_id] = ball
34 | elif isinstance(balls, SporeBall):
35 | self.balls[balls.ball_id] = balls
36 | return True
37 |
38 | def remove_balls(self, balls):
39 | if isinstance(balls, list):
40 | for ball in balls:
41 | ball.remove()
42 | try:
43 | del self.balls[ball.ball_id]
44 | except:
45 | pass
46 | elif isinstance(balls, SporeBall):
47 | balls.remove()
48 | try:
49 | del self.balls[balls.ball_id]
50 | except:
51 | pass
52 |
53 | def spawn_ball(self, position=None):
54 | if position is None:
55 | position = self.border.sample()
56 | name = uuid.uuid1()
57 | return SporeBall(name=name, position=position, border=self.border, score=self.ball_settings.score_init,
58 | direction=Vector2(1,0))
59 |
60 | def init_balls(self, custom_init=None):
61 | # [position.x, position.y, score, direction.x, direction.y, vel.x, vel.y, acc.x, acc.y,
62 | # move_time, moving]
63 | if custom_init is not None:
64 | for ball_cfg in custom_init:
65 | ball = self.spawn_ball(position=Vector2(*ball_cfg[:2]))
66 | if len(ball_cfg) > 2:
67 | ball.direction = Vector2(*ball_cfg[2:4])
68 | ball.vel = Vector2(*ball_cfg[4:6])
69 | ball.move_frame = ball_cfg[6]
70 | ball.moving = ball_cfg[7]
71 | ball.owner = ball_cfg[8]
72 | self.balls[ball.name] = ball
73 |
74 | def step(self, duration):
75 | return
76 |
77 | def reset(self):
78 | self.balls = {}
79 | return True
80 |
--------------------------------------------------------------------------------
/gobigger/managers/tests/test_base_manager.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.managers import BaseManager
7 | from gobigger.utils import Border
8 | from gobigger.server import Server
9 |
10 | logging.basicConfig(level=logging.DEBUG)
11 |
12 | @pytest.mark.unittest
13 | class TestBaseManager:
14 |
15 | def test_init(self):
16 | cfg = Server.default_config()
17 | border = Border(0, 0, 100, 100)
18 | base_manager = BaseManager(cfg=cfg.manager_settings.food_manager, border=border)
19 | assert True
20 |
21 | def test_others(self):
22 | cfg = Server.default_config()
23 | border = Border(0, 0, 100, 100)
24 | base_manager = BaseManager(cfg=cfg.manager_settings.food_manager, border=border)
25 | base_manager.get_balls()
26 | with pytest.raises(Exception) as e:
27 | base_manager.add_balls([])
28 | with pytest.raises(Exception) as e:
29 | base_manager.refresh()
30 | with pytest.raises(Exception) as e:
31 | base_manager.remove_balls(balls=None)
32 | with pytest.raises(Exception) as e:
33 | base_manager.spawn_ball()
34 | with pytest.raises(Exception) as e:
35 | base_manager.init_balls()
36 | with pytest.raises(Exception) as e:
37 | base_manager.step(duration=None)
38 | with pytest.raises(Exception) as e:
39 | base_manager.obs()
40 | with pytest.raises(Exception) as e:
41 | base_manager.reset()
42 |
43 |
44 |
--------------------------------------------------------------------------------
/gobigger/managers/tests/test_food_manager.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.managers import FoodManager
7 | from gobigger.utils import Border
8 | from gobigger.server import Server
9 |
10 | logging.basicConfig(level=logging.DEBUG)
11 |
12 | @pytest.mark.unittest
13 | class TestFoodManager:
14 |
15 | def get_manager(self):
16 | cfg = Server.default_config()
17 | border = Border(0, 0, cfg.map_width, cfg.map_height)
18 | food_manager = FoodManager(cfg=cfg.manager_settings.food_manager, border=border)
19 | return food_manager
20 |
21 | def test_init(self):
22 | food_manager = self.get_manager()
23 | assert True
24 |
25 | def test_get_balls(self):
26 | food_manager = self.get_manager()
27 | food_manager.init_balls()
28 | balls = food_manager.get_balls()
29 | assert len(balls) == food_manager.cfg.num_init
30 | for i in range(10):
31 | logging.debug(balls[i])
32 | assert True
33 |
34 | def test_remove_balls(self):
35 | food_manager = self.get_manager()
36 | food_manager.init_balls()
37 | balls = food_manager.get_balls()
38 | assert len(balls) == food_manager.cfg.num_init
39 | food_manager.remove_balls(balls[:100])
40 | logging.debug('[FoodManager.remove_balls] init num: {}, now num {}'
41 | .format(food_manager.cfg.num_init, len(food_manager.get_balls())))
42 | assert True
43 |
44 | def test_step(self):
45 | food_manager = self.get_manager()
46 | food_manager.init_balls()
47 | balls = food_manager.get_balls()
48 | assert len(balls) == food_manager.cfg.num_init
49 | food_manager.remove_balls(balls[:100])
50 | logging.debug('[FoodManager.remove_balls] init num: {}, now num {}'
51 | .format(food_manager.cfg.num_init, len(food_manager.get_balls())))
52 | refresh_frame_freq = food_manager.cfg.refresh_frame_freq
53 | logging.debug('=================== test step ===================')
54 | for i in range(10):
55 | food_manager.step(duration=None)
56 | logging.debug('[FoodManager.step] {} food num = {}'.format(i, len(food_manager.get_balls())))
57 |
58 | def test_reset(self):
59 | food_manager = self.get_manager()
60 | food_manager.init_balls()
61 | balls = food_manager.get_balls()
62 | assert len(balls) == food_manager.cfg.num_init
63 | food_manager.reset()
64 | balls = food_manager.get_balls()
65 | assert len(balls) == 0
66 |
67 | def test_add_balls(self):
68 | to_add_list = []
69 | food_manager = self.get_manager()
70 | for _ in range(2):
71 | to_add_list.append(food_manager.spawn_ball())
72 | assert food_manager.add_balls(to_add_list)
73 |
74 | def test_init_balls_custom(self):
75 | custom_init = [[100, 100, 2]]
76 | food_manager = self.get_manager()
77 | food_manager.init_balls(custom_init)
78 |
--------------------------------------------------------------------------------
/gobigger/managers/tests/test_spore_manager.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.managers import SporeManager
7 | from gobigger.balls import SporeBall
8 | from gobigger.utils import Border
9 | from gobigger.server import Server
10 |
11 | logging.basicConfig(level=logging.DEBUG)
12 |
13 | @pytest.mark.unittest
14 | class TestSporeManager:
15 |
16 | def get_manager(self):
17 | cfg = Server.default_config()
18 | border = Border(0, 0, cfg.map_width, cfg.map_height)
19 | spore_manager = SporeManager(cfg=cfg.manager_settings.spore_manager, border=border)
20 | return spore_manager
21 |
22 | def get_spore_ball(self):
23 | ball_id = uuid.uuid1()
24 | border = Border(0, 0, 1000, 1000)
25 | position = Vector2(100, 100)
26 | score = SporeBall.default_config().score_init
27 | direction = Vector2(1, 0)
28 | return SporeBall(ball_id, position, border=border, score=score, direction=direction)
29 |
30 | def test_init(self):
31 | spore_manager = self.get_manager()
32 | assert True
33 |
34 | def test_get_balls(self):
35 | spore_manager = self.get_manager()
36 | for i in range(10):
37 | spore_manager.add_balls(self.get_spore_ball())
38 | spore_manager.add_balls([self.get_spore_ball(), self.get_spore_ball()])
39 | balls = spore_manager.get_balls()
40 | for i in range(10):
41 | logging.debug(balls[i])
42 | assert True
43 |
44 | def test_remove_balls(self):
45 | spore_manager = self.get_manager()
46 | for i in range(10):
47 | spore_manager.add_balls(self.get_spore_ball())
48 | balls = spore_manager.get_balls()
49 | original_len = len(balls)
50 | spore_manager.remove_balls(balls[:5])
51 | logging.debug('[SporeManager.remove_balls] init num: {}, now num {}'
52 | .format(original_len, len(spore_manager.get_balls())))
53 | assert True
54 |
55 | def test_reset(self):
56 | spore_manager = self.get_manager()
57 | for i in range(10):
58 | spore_manager.add_balls(self.get_spore_ball())
59 | balls = spore_manager.get_balls()
60 | spore_manager.reset()
61 | assert len(spore_manager.balls) == 0
62 |
--------------------------------------------------------------------------------
/gobigger/managers/tests/test_thorns_manager.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.managers import ThornsManager
7 | from gobigger.utils import Border
8 | from gobigger.server import Server
9 |
10 | logging.basicConfig(level=logging.DEBUG)
11 |
12 | @pytest.mark.unittest
13 | class TestThornsManager:
14 |
15 | def get_manager(self):
16 | cfg = Server.default_config()
17 | border = Border(0, 0, cfg.map_width, cfg.map_height)
18 | thorns_manager = ThornsManager(cfg=cfg.manager_settings.thorns_manager, border=border)
19 | return thorns_manager
20 |
21 | def test_init(self):
22 | thorns_manager = self.get_manager()
23 | assert True
24 |
25 | def test_get_balls(self):
26 | thorns_manager = self.get_manager()
27 | thorns_manager.init_balls()
28 | balls = thorns_manager.get_balls()
29 | assert len(balls) == thorns_manager.cfg.num_init
30 | for i in range(2):
31 | logging.debug(balls[i])
32 | assert True
33 |
34 | def test_remove_balls(self):
35 | thorns_manager = self.get_manager()
36 | thorns_manager.init_balls()
37 | balls = thorns_manager.get_balls()
38 | assert len(balls) == thorns_manager.cfg.num_init
39 | thorns_manager.remove_balls(balls[:20])
40 | logging.debug('[ThornsManager.remove_balls] init num: {}, now num {}'
41 | .format(thorns_manager.cfg.num_init, len(thorns_manager.get_balls())))
42 | assert True
43 |
44 | def test_step(self):
45 | thorns_manager = self.get_manager()
46 | thorns_manager.init_balls()
47 | balls = thorns_manager.get_balls()
48 | assert len(balls) == thorns_manager.cfg.num_init
49 | thorns_manager.remove_balls(balls[:20])
50 | logging.debug('[ThornsManager.remove_balls] init num: {}, now num {}'
51 | .format(thorns_manager.cfg.num_init, len(thorns_manager.get_balls())))
52 | refresh_frame_freq = thorns_manager.cfg.refresh_frame_freq
53 | logging.debug('=================== test step ===================')
54 | for i in range(20):
55 | thorns_manager.step(duration=None)
56 | logging.debug('[FoodManager.step] {} food num = {}'.format(i, len(thorns_manager.get_balls())))
57 |
58 | def test_reset(self):
59 | thorns_manager = self.get_manager()
60 | thorns_manager.init_balls()
61 | balls = thorns_manager.get_balls()
62 | assert len(balls) == thorns_manager.cfg.num_init
63 | thorns_manager.reset()
64 | assert len(thorns_manager.balls) == 0
65 |
66 | def test_add_remove_list(self):
67 | thorns_manager = self.get_manager()
68 | thorns_manager.init_balls()
69 | balls = thorns_manager.get_balls()
70 | thorns_manager.add_balls(balls)
71 | thorns_manager.remove_balls(balls)
72 |
--------------------------------------------------------------------------------
/gobigger/managers/thorns_manager.py:
--------------------------------------------------------------------------------
1 | import math
2 | import logging
3 | import random
4 | import uuid
5 | from abc import ABC, abstractmethod
6 | from easydict import EasyDict
7 | from pygame.math import Vector2
8 |
9 | from .base_manager import BaseManager
10 | from gobigger.utils import format_vector, Border, SequenceGenerator
11 | from gobigger.balls import FoodBall, ThornsBall, CloneBall, SporeBall
12 |
13 |
14 | class ThornsManager(BaseManager):
15 |
16 | def __init__(self, cfg, border, random_generator=None, sequence_generator=None):
17 | super(ThornsManager, self).__init__(cfg, border)
18 | self.refresh_frame_freq = self.cfg.refresh_frame_freq
19 | self.refresh_frame_count = 0
20 | if random_generator is not None:
21 | self._random = random_generator
22 | else:
23 | self._random = random.Random()
24 | if sequence_generator is not None:
25 | self.sequence_generator = sequence_generator
26 | else:
27 | self.sequence_generator = SequenceGenerator()
28 |
29 | def get_balls(self):
30 | return list(self.balls.values())
31 |
32 | def add_balls(self, balls):
33 | if isinstance(balls, list):
34 | for ball in balls:
35 | self.balls[ball.ball_id] = ball
36 | elif isinstance(balls, ThornsBall):
37 | self.balls[balls.ball_id] = balls
38 | return True
39 |
40 | def refresh(self):
41 | left_num = self.cfg.num_max - len(self.balls)
42 | todo_num = min(math.ceil(self.cfg.refresh_percent * left_num), left_num)
43 | new_balls = {}
44 | for _ in range(todo_num):
45 | ball = self.spawn_ball()
46 | self.add_balls(ball)
47 | new_balls[ball.ball_id] = ball.save()
48 | return new_balls
49 |
50 | def remove_balls(self, balls):
51 | if isinstance(balls, list):
52 | for ball in balls:
53 | ball.remove()
54 | try:
55 | del self.balls[ball.ball_id]
56 | except:
57 | pass
58 | elif isinstance(balls, ThornsBall):
59 | balls.remove()
60 | try:
61 | del self.balls[balls.ball_id]
62 | except:
63 | pass
64 |
65 | def spawn_ball(self, position=None, score=None):
66 | if position is None:
67 | position = self.border.sample()
68 | if score is None:
69 | score = self._random.uniform(self.ball_settings.score_min, self.ball_settings.score_max)
70 | ball_id = self.sequence_generator.get()
71 | return ThornsBall(ball_id=ball_id, position=position, border=self.border, score=score, **self.ball_settings)
72 |
73 | def init_balls(self, custom_init=None):
74 | # [position.x, position.y, score, vel.x, vel.y, acc.x, acc.y,
75 | # move_time, moving]
76 | if custom_init is None or len(custom_init) == 0:
77 | for _ in range(self.cfg.num_init):
78 | ball = self.spawn_ball()
79 | self.balls[ball.ball_id] = ball
80 | else:
81 | for ball_cfg in custom_init:
82 | ball = self.spawn_ball(position=Vector2(*ball_cfg[:2]), score=ball_cfg[2])
83 | if len(ball_cfg) > 3:
84 | ball.vel = Vector2(*ball_cfg[3:5])
85 | ball.move_frame = Vector2(*ball_cfg[5])
86 | ball.moving = ball_cfg[6]
87 | self.balls[ball.ball_id] = ball
88 |
89 | def step(self, duration):
90 | self.refresh_frame_count += 1
91 | new_balls = {}
92 | if self.refresh_frame_count > self.refresh_frame_freq:
93 | new_balls = self.refresh()
94 | self.refresh_frame_count = 0
95 | return new_balls
96 |
97 | def reset(self):
98 | self.refresh_frame_count = 0
99 | self.balls = {}
100 | return True
101 |
--------------------------------------------------------------------------------
/gobigger/playbacks/__init__.py:
--------------------------------------------------------------------------------
1 | import importlib
2 |
3 | from .base_pb import BasePB
4 | from .null_pb import NullPB
5 | from .video_pb import VideoPB
6 | from .frame_pb import FramePB
7 | from .action_pb import ActionPB
8 |
9 |
10 | def create_pb(playback_settings, **kwargs):
11 | playback_type = playback_settings.playback_type
12 | if playback_type == 'none':
13 | return NullPB(None)
14 | elif playback_type == 'by_video':
15 | return VideoPB(playback_settings['by_video'], **kwargs)
16 | elif playback_type == 'by_frame':
17 | return FramePB(playback_settings['by_frame'], **kwargs)
18 | elif playback_type == 'by_action':
19 | return ActionPB(playback_settings['by_action'], **kwargs)
20 | else:
21 | raise NotImplementedError
22 |
--------------------------------------------------------------------------------
/gobigger/playbacks/action_pb.py:
--------------------------------------------------------------------------------
1 | import os
2 | import cv2
3 | import numpy as np
4 | import logging
5 | import uuid
6 | import copy
7 | import pickle
8 | import lz4.frame
9 |
10 | from .base_pb import BasePB
11 |
12 |
13 | class ActionPB(BasePB):
14 |
15 | def __init__(self, playback_settings, **kwargs):
16 | self.playback_settings = playback_settings
17 | self.save_action = self.playback_settings.save_action
18 | self.save_dir = self.playback_settings.save_dir
19 | self.save_name_prefix = self.playback_settings.save_name_prefix
20 | if self.save_action:
21 | if not os.path.isdir(self.save_dir):
22 | try:
23 | os.makedirs(self.save_dir)
24 | except:
25 | pass
26 | logging.warning('save_dir={} must be an existed directory!'.format(self.save_dir))
27 | if not self.save_name_prefix:
28 | self.save_name_prefix = str(uuid.uuid1())
29 | self.playback_data = {}
30 | logging.warning('`by_action` is not available now, please use `by_video` or `by_frame`.')
31 |
32 | def need_save(self, *args, **kwargs):
33 | return self.save_action
34 |
35 | def save_step(self, actions, last_frame_count):
36 | self.playback_data[last_frame_count] = actions
37 |
38 | def save_final(self, cfg, seed):
39 | self.playback_data['cfg'] = cfg
40 | self.playback_data['seed'] = seed
41 | self.playback_path = os.path.join(self.save_dir, self.save_name_prefix + '.ac')
42 | compressed_data = lz4.frame.compress(pickle.dumps(self.playback_data))
43 | with open(self.playback_path, 'wb') as f:
44 | pickle.dump(compressed_data, f)
45 | logging.info('save ac at {}'.format(self.playback_path))
46 |
--------------------------------------------------------------------------------
/gobigger/playbacks/base_pb.py:
--------------------------------------------------------------------------------
1 | class BasePB:
2 |
3 | def __init__(self, playback_settings):
4 | self.playback_settings = playback_settings
5 |
6 | def need_save(self, last_frame_count, *args, **kwargs):
7 | raise NotImplementedError
8 |
9 | def save_step(self, *args, **kwargs):
10 | raise NotImplementedError
11 |
12 | def save_final(self, *args, **kwargs):
13 | raise NotImplementedError
14 |
--------------------------------------------------------------------------------
/gobigger/playbacks/frame_pb.py:
--------------------------------------------------------------------------------
1 | import os
2 | import cv2
3 | import numpy as np
4 | import logging
5 | import uuid
6 | import copy
7 | import pickle
8 | import lz4.frame
9 |
10 | from .base_pb import BasePB
11 |
12 |
13 | class FramePB(BasePB):
14 |
15 | def __init__(self, playback_settings, **kwargs):
16 | self.playback_settings = playback_settings
17 | self.save_frame = self.playback_settings.save_frame
18 | self.save_all = self.playback_settings.save_all
19 | self.save_partial = self.playback_settings.save_partial
20 | self.save_dir = self.playback_settings.save_dir
21 | self.save_name_prefix = self.playback_settings.save_name_prefix
22 | if self.save_frame:
23 | if not os.path.isdir(self.save_dir):
24 | try:
25 | os.makedirs(self.save_dir)
26 | except:
27 | pass
28 | logging.warning('save_dir={} must be an existed directory!'.format(self.save_dir))
29 | if not self.save_name_prefix:
30 | self.save_name_prefix = str(uuid.uuid1())
31 | self.playback_data = {}
32 |
33 | def need_save(self, *args, **kwargs):
34 | return self.save_frame
35 |
36 | def save_step(self, diff_balls_remove, diff_balls_modify, leaderboard, last_frame_count, *args, **kwargs):
37 | self.playback_data[last_frame_count] = [diff_balls_modify, diff_balls_remove, leaderboard]
38 |
39 | def save_final(self, cfg, *args, **kwargs):
40 | if self.save_frame:
41 | self.playback_data['cfg'] = cfg
42 | self.playback_path = os.path.join(self.save_dir, self.save_name_prefix + '.pb')
43 | compressed_data = lz4.frame.compress(pickle.dumps(self.playback_data))
44 | with open(self.playback_path, 'wb') as f:
45 | pickle.dump(compressed_data, f)
46 | logging.info('save pb at {}'.format(self.playback_path))
47 |
48 |
--------------------------------------------------------------------------------
/gobigger/playbacks/null_pb.py:
--------------------------------------------------------------------------------
1 | from .base_pb import BasePB
2 |
3 |
4 | class NullPB(BasePB):
5 |
6 | def need_save(self, *args, **kwargs):
7 | return False
8 |
9 | def save_step(self, *args, **kwargs):
10 | return
11 |
12 | def save_final(self, *args, **kwargs):
13 | return
14 |
--------------------------------------------------------------------------------
/gobigger/playbacks/tests/test_playback.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 | import pytest
4 | import uuid
5 | from pygame.math import Vector2
6 | import pygame
7 | import random
8 | import time
9 | from easydict import EasyDict
10 |
11 | from gobigger.envs import create_env
12 | from gobigger.agents import BotAgent
13 |
14 | logging.basicConfig(level=logging.DEBUG)
15 |
16 |
17 | @pytest.mark.unittest
18 | class TestPlayback:
19 |
20 | def test_none_pb(self):
21 | env = create_env('st_t2p2', dict(
22 | frame_limit=100,
23 | playback_settings=dict(
24 | playback_type='none',
25 | ),
26 | ))
27 | obs = env.reset()
28 | bot_agents = []
29 | team_infos = env.get_team_infos()
30 | print(team_infos)
31 | for team_id, player_ids in team_infos:
32 | for player_id in player_ids:
33 | bot_agents.append(BotAgent(player_id, level=2))
34 | time_step_all = 0
35 | for i in range(100000):
36 | actions = {bot_agent.name: bot_agent.step(obs[1][bot_agent.name]) for bot_agent in bot_agents}
37 | t1 = time.time()
38 | obs, reward, done, info = env.step(actions=actions)
39 | t2 = time.time()
40 | time_step_all += t2-t1
41 | logging.debug('{} {:.4f} envstep {:.3f} / {:.3f}, leaderboard={}'\
42 | .format(i, obs[0]['last_frame_count'], t2-t1, time_step_all/(i+1), obs[0]['leaderboard']))
43 | if done:
44 | logging.debug('Game Over')
45 | break
46 | env.close()
47 |
48 | def test_video_pb(self):
49 | env = create_env('st_t2p2', dict(
50 | frame_limit=100,
51 | playback_settings=dict(
52 | playback_type='by_video',
53 | by_video=dict(
54 | save_video=True,
55 | ),
56 | ),
57 | ))
58 | obs = env.reset()
59 | bot_agents = []
60 | team_infos = env.get_team_infos()
61 | print(team_infos)
62 | for team_id, player_ids in team_infos:
63 | for player_id in player_ids:
64 | bot_agents.append(BotAgent(player_id, level=2))
65 | time_step_all = 0
66 | for i in range(100000):
67 | actions = {bot_agent.name: bot_agent.step(obs[1][bot_agent.name]) for bot_agent in bot_agents}
68 | t1 = time.time()
69 | obs, reward, done, info = env.step(actions=actions)
70 | t2 = time.time()
71 | time_step_all += t2-t1
72 | logging.debug('{} {:.4f} envstep {:.3f} / {:.3f}, leaderboard={}'\
73 | .format(i, obs[0]['last_frame_count'], t2-t1, time_step_all/(i+1), obs[0]['leaderboard']))
74 | if done:
75 | logging.debug('Game Over')
76 | break
77 | env.close()
78 | assert os.path.isfile('test-all.mp4')
79 | os.remove('test-all.mp4')
80 |
81 | def test_frame_pb(self):
82 | env = create_env('st_t2p2', dict(
83 | frame_limit=100,
84 | playback_settings=dict(
85 | playback_type='by_frame',
86 | by_frame=dict(
87 | save_frame=True,
88 | )
89 | ),
90 | ))
91 | obs = env.reset()
92 | bot_agents = []
93 | team_infos = env.get_team_infos()
94 | print(team_infos)
95 | for team_id, player_ids in team_infos:
96 | for player_id in player_ids:
97 | bot_agents.append(BotAgent(player_id, level=2))
98 | time_step_all = 0
99 | for i in range(100000):
100 | actions = {bot_agent.name: bot_agent.step(obs[1][bot_agent.name]) for bot_agent in bot_agents}
101 | t1 = time.time()
102 | obs, reward, done, info = env.step(actions=actions)
103 | t2 = time.time()
104 | time_step_all += t2-t1
105 | logging.debug('{} {:.4f} envstep {:.3f} / {:.3f}, leaderboard={}'\
106 | .format(i, obs[0]['last_frame_count'], t2-t1, time_step_all/(i+1), obs[0]['leaderboard']))
107 | if done:
108 | logging.debug('Game Over')
109 | break
110 | env.close()
111 | assert os.path.isfile('test.pb')
112 | os.remove('test.pb')
113 |
--------------------------------------------------------------------------------
/gobigger/playbacks/video_pb.py:
--------------------------------------------------------------------------------
1 | import os
2 | import cv2
3 | import numpy as np
4 | import logging
5 | import uuid
6 | import copy
7 | import pickle
8 |
9 | from gobigger.render import EnvRender
10 | from .base_pb import BasePB
11 |
12 |
13 | class VideoPB(BasePB):
14 |
15 | def __init__(self, playback_settings, **kwargs):
16 | self.playback_settings = playback_settings
17 | self.fps = kwargs['fps']
18 | self.map_width = kwargs['map_width']
19 | self.map_height = kwargs['map_height']
20 | self.save_video = self.playback_settings.save_video
21 | self.save_fps = self.playback_settings.save_fps
22 | self.save_resolution = self.playback_settings.save_resolution
23 | self.save_all = self.playback_settings.save_all
24 | self.save_partial = self.playback_settings.save_partial
25 | self.save_dir = self.playback_settings.save_dir
26 | self.save_name_prefix = self.playback_settings.save_name_prefix
27 | if self.save_video:
28 | if not os.path.isdir(self.save_dir):
29 | try:
30 | os.makedirs(self.save_dir)
31 | except:
32 | pass
33 | logging.warning('save_dir={} must be an existed directory!'.format(self.save_dir))
34 | if not self.save_name_prefix:
35 | self.save_name_prefix = str(uuid.uuid1())
36 | self.save_fps = int(self.save_fps)
37 | self.save_resolution = int(self.save_resolution)
38 | self.save_freq = self.fps // self.save_fps
39 | self.render = EnvRender(game_screen_width=self.save_resolution, game_screen_height=self.save_resolution,
40 | map_width=self.map_width, map_height=self.map_width)
41 | self.screens_all = []
42 | self.screens_partial = []
43 |
44 | def get_clip_screen(self, screen_data, rectangle):
45 | rectangle_tmp = copy.deepcopy(rectangle)
46 | left_top_x, left_top_y, right_bottom_x, right_bottom_y = rectangle_tmp
47 | left_top_x_fix = max(left_top_x, 0)
48 | left_top_y_fix = max(left_top_y, 0)
49 | right_bottom_x_fix = min(right_bottom_x, self.width)
50 | right_bottom_y_fix = min(right_bottom_y, self.height)
51 |
52 | if len(screen_data.shape) == 3:
53 | screen_data_clip = screen_data[left_top_x_fix:right_bottom_x_fix,
54 | left_top_y_fix:right_bottom_y_fix, :]
55 | screen_data_clip = np.pad(screen_data_clip,
56 | ((left_top_x_fix-left_top_x,right_bottom_x-right_bottom_x_fix),
57 | (left_top_y_fix-left_top_y,right_bottom_y-right_bottom_y_fix),
58 | (0,0)),
59 | mode='constant')
60 | elif len(screen_data.shape) == 2:
61 | screen_data_clip = screen_data[left_top_x_fix:right_bottom_x_fix,
62 | left_top_y_fix:right_bottom_y_fix]
63 | screen_data_clip = np.pad(screen_data_clip,
64 | ((left_top_x_fix-left_top_x,right_bottom_x-right_bottom_x_fix),
65 | (left_top_y_fix-left_top_y,right_bottom_y-right_bottom_y_fix)),
66 | mode='constant')
67 | else:
68 | raise NotImplementedError
69 | return screen_data_clip
70 |
71 | def need_save(self, last_frame_count, *args, **kwargs):
72 | return self.save_video and last_frame_count % self.save_freq == 0
73 |
74 | def save_step(self, food_balls, thorns_balls, spore_balls, players, player_num_per_team, *args, **kwargs):
75 | self.screens_all.append(self.render.get_screen(food_balls, thorns_balls, spore_balls, players, player_num_per_team))
76 |
77 | def save_final(self, *args, **kwargs):
78 | if self.save_video:
79 | if self.save_all:
80 | video_file_all = os.path.join(self.save_dir, '{}-all.mp4'.format(self.save_name_prefix))
81 | out = cv2.VideoWriter(video_file_all, cv2.VideoWriter_fourcc(*'mp4v'), self.save_fps,
82 | (self.screens_all[0].shape[1], self.screens_all[0].shape[0]))
83 | for index, screen in enumerate(self.screens_all):
84 | out.write(screen)
85 | out.release()
86 | cv2.destroyAllWindows()
87 | if self.save_partial:
88 | for player_id, screens in self.screens_partial.items():
89 | video_file_partial = os.path.join(self.save_dir, '{}-{:02d}.mp4'.format(self.save_name_prefix, player_id))
90 | out = cv2.VideoWriter(video_file_partial, cv2.VideoWriter_fourcc(*'mp4v'), self.save_fps,
91 | (screens[0].shape[1], screens[0].shape[0]))
92 | for index, screen in enumerate(self.screens):
93 | if index % self.save_freq == 0:
94 | out.write(screen)
95 | out.release()
96 | cv2.destroyAllWindows()
97 |
--------------------------------------------------------------------------------
/gobigger/players/__init__.py:
--------------------------------------------------------------------------------
1 | from .base_player import BasePlayer
2 | from .human_player import HumanPlayer
3 | from .human_sp_player import HumanSPPlayer
--------------------------------------------------------------------------------
/gobigger/players/base_player.py:
--------------------------------------------------------------------------------
1 | from gobigger.balls import FoodBall, ThornsBall, CloneBall, SporeBall
2 |
3 |
4 | class BasePlayer:
5 | '''
6 | Player's abstract class
7 | '''
8 | def __init__(self, name=None):
9 | self.name = name
10 |
11 | def move(self, direction):
12 | '''
13 | Parameters:
14 | direction : Given any point in a unit circle, the angle represents the direction, and the magnitude represents the acceleration
15 | '''
16 | raise NotImplementedError
17 |
18 | def eject(self):
19 | '''
20 | Do sporulation
21 | '''
22 | raise NotImplementedError
23 |
24 | def eat(self, ball):
25 | '''
26 | Eat another ball
27 | '''
28 | raise NotImplementedError
29 |
30 | def stop(self):
31 | '''
32 | stop moving
33 | '''
34 | raise NotImplementedError
35 |
36 | def respawn(self):
37 | raise NotImplementedError
--------------------------------------------------------------------------------
/gobigger/players/human_sp_player.py:
--------------------------------------------------------------------------------
1 | from gobigger.balls import FoodBall, ThornsBall, CloneBall, SporeBall
2 | from .human_player import HumanPlayer
3 |
4 |
5 | class HumanSPPlayer(HumanPlayer):
6 |
7 | def __init__(self, cfg, team_id, player_id, border, spore_settings, sequence_generator=None):
8 | super(HumanSPPlayer, self).__init__(cfg, team_id, player_id, border, spore_settings)
9 | assert sequence_generator is not None
10 | self.sequence_generator = sequence_generator
11 |
12 | def move(self, ball_id=None, direction=None, duration=0.05):
13 | if ball_id is None:
14 | for ball_id, ball in self.balls.items():
15 | ball.move(given_acc=direction, duration=duration)
16 | ball.score_decay()
17 | else:
18 | if ball_id in self.balls:
19 | self.balls[ball_id].move(given_acc=direction, duration=duration)
20 | self.balls[ball_id].score_decay()
21 |
22 | def eject(self, ball_id=None, direction=None):
23 | ret = []
24 | if ball_id and ball_id in self.balls:
25 | ret.append(self.balls[ball_id].eject(direction=direction))
26 | return ret
27 |
28 | def split(self, ball_id=None, direction=None):
29 | if ball_id and ball_id in self.balls:
30 | ret = self.balls[ball_id].split(self.get_clone_num(), direction=direction)
31 | if ret and isinstance(ret, CloneBall):
32 | self.add_balls(ret)
33 | return True
34 |
35 | def respawn(self, position):
36 | ball = CloneBall(ball_id=self.sequence_generator.get(), position=position, border=self.border,
37 | score=self.ball_settings.score_respawn, team_id=self.team_id,
38 | player_id=self.player_id, spore_settings=self.spore_settings,
39 | sequence_generator=self.sequence_generator, **self.ball_settings)
40 | self.balls = {}
41 | self.balls[ball.ball_id] = ball
42 | return True
43 |
--------------------------------------------------------------------------------
/gobigger/players/tests/test_base_player.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 |
6 | from gobigger.players import BasePlayer
7 | from gobigger.balls import ThornsBall, SporeBall, CloneBall, FoodBall
8 | from gobigger.utils import Border
9 | from gobigger.server import Server
10 |
11 | logging.basicConfig(level=logging.DEBUG)
12 |
13 | @pytest.mark.unittest
14 | class TestBasePlayer:
15 |
16 | def test_all(self):
17 | base_player = BasePlayer(name='test')
18 | with pytest.raises(Exception) as e:
19 | base_player.move(direction=None)
20 | with pytest.raises(Exception) as e:
21 | base_player.eject()
22 | with pytest.raises(Exception) as e:
23 | base_player.eat(ball=None)
24 | with pytest.raises(Exception) as e:
25 | base_player.stop()
26 | with pytest.raises(Exception) as e:
27 | base_player.respawn()
28 |
--------------------------------------------------------------------------------
/gobigger/render/__init__.py:
--------------------------------------------------------------------------------
1 | from .base_render import BaseRender
2 | from .env_render import EnvRender
3 | from .realtime_render import RealtimeRender, RealtimePartialRender
4 | from .pb_render import PBRender, TkSelect
5 |
--------------------------------------------------------------------------------
/gobigger/render/base_render.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pygame
3 | import platform
4 |
5 |
6 | class BaseRender:
7 |
8 | def __init__(self, game_screen_width, game_screen_height, info_width=0, info_height=0, with_show=False):
9 | pygame.init()
10 | if platform.system() == 'Linux': # If the current system is linux, window is not used
11 | os.environ["SDL_VIDEODRIVER"] = "dummy"
12 | self.game_screen_width = game_screen_width
13 | self.game_screen_height = game_screen_height
14 | self.total_screen_width = game_screen_width + info_width
15 | self.total_screen_height = game_screen_height + info_height
16 | # self.FPS = 60 # Set the frame rate (the number of screen refreshes per second)
17 | # self.fpsClock = pygame.time.Clock()
18 | if with_show:
19 | self.screen = pygame.display.set_mode((self.total_screen_width, self.total_screen_height), 0, 32)
20 | pygame.display.set_caption("GoBigger - OpenDILab Environment")
21 |
22 | def fill(self, server):
23 | raise NotImplementedError
24 |
25 | def show(self):
26 | raise NotImplementedError
27 |
28 | def close(self):
29 | raise NotImplementedError
30 |
--------------------------------------------------------------------------------
/gobigger/render/tests/test_env_render.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 | import pygame
6 | import random
7 | from easydict import EasyDict
8 |
9 | from gobigger.balls import BaseBall, SporeBall
10 | from gobigger.players import HumanPlayer
11 | from gobigger.utils import Border
12 | from gobigger.server import Server
13 | from gobigger.render import EnvRender
14 |
15 | logging.basicConfig(level=logging.DEBUG)
16 |
17 |
18 | @pytest.mark.unittest
19 | class TestEnvRender:
20 |
21 | def test_init(self):
22 | render = EnvRender()
23 | assert True
24 |
25 | def test_fill_all(self):
26 | border = Border(0, 0, 1000, 1000)
27 | render = EnvRender()
28 | food_balls = [BaseBall('0', border.sample(), border=border, score=100)]
29 | thorns_balls = [BaseBall('0', border.sample(), border=border, score=10000)]
30 | spore_balls = [BaseBall('0', border.sample(), border=border, score=1400)]
31 | players = [HumanPlayer(cfg=Server.default_config().manager_settings.player_manager.ball_settings,
32 | team_id=0, player_id=0, border=border,
33 | spore_settings=Server.default_config().manager_settings.spore_manager.ball_settings)]
34 | screen_data_all = render.get_screen(food_balls, thorns_balls, spore_balls, players, 1)
35 | assert len(screen_data_all.shape) == 3
36 |
--------------------------------------------------------------------------------
/gobigger/render/tests/test_realtime_render.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 | import pygame
6 | import random
7 |
8 | from gobigger.balls import BaseBall
9 | from gobigger.players import HumanPlayer
10 | from gobigger.utils import Border
11 | from gobigger.server import Server
12 | from gobigger.render import RealtimeRender, RealtimePartialRender
13 |
14 | logging.basicConfig(level=logging.DEBUG)
15 |
16 |
17 | @pytest.mark.unittest
18 | class TestRealtimePartialRender:
19 |
20 | def test_init(self):
21 | render = RealtimeRender()
22 | render = RealtimePartialRender()
23 | assert True
24 |
--------------------------------------------------------------------------------
/gobigger/server/__init__.py:
--------------------------------------------------------------------------------
1 | from .server import Server
2 | from .server_sp import ServerSP
3 |
--------------------------------------------------------------------------------
/gobigger/server/tests/test_server.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 | import pygame
6 | import random
7 | import numpy as np
8 | import cv2
9 | import multiprocessing as mp
10 |
11 | from gobigger.utils import Border
12 | from gobigger.server import Server
13 | from gobigger.render import RealtimeRender, RealtimePartialRender, EnvRender
14 |
15 | logging.basicConfig(level=logging.DEBUG)
16 |
17 |
18 | @pytest.mark.unittest
19 | class TestServer:
20 |
21 | def test_init(self):
22 | server = Server()
23 | assert True
24 |
25 | def test_spawn_balls(self):
26 | server = Server()
27 | server.reset()
28 |
29 | def test_step_control_random(self):
30 | server = Server()
31 | server.reset()
32 | fps_set = 20
33 | clock = pygame.time.Clock()
34 | render = RealtimePartialRender()
35 | for i in range(10):
36 | actions = {player_name: [random.uniform(-1, 1), random.uniform(-1, 1), -1] \
37 | for player_name in server.get_player_names()}
38 | done = server.step(actions=actions)
39 | obs = server.obs()
40 | render.fill(obs[0], obs[1][0], player_num_per_team=1, fps=10)
41 | render.show()
42 | clock.tick(fps_set)
43 | server.close()
44 |
45 | def test_obs(self):
46 | server = Server()
47 | server.reset()
48 | for i in range(10):
49 | actions = {player_name: [random.uniform(-1, 1), random.uniform(-1, 1), -1] \
50 | for player_name in server.get_player_names()}
51 | done = server.step(actions=actions)
52 | obs = server.obs()
53 | logging.debug(obs[0])
54 |
55 | def test_obs_multi_player(self):
56 | server = Server(dict(
57 | team_num=1,
58 | player_num_per_team=2,
59 | ))
60 | server.reset()
61 | for i in range(10):
62 | actions = {player_name: [random.uniform(-1, 1), random.uniform(-1, 1), -1] \
63 | for player_name in server.get_player_names()}
64 | done = server.step(actions=actions)
65 | obs = server.obs()
66 | logging.debug(obs[0])
67 |
68 | def test_multiprocessing(self):
69 | '''
70 | Overview:
71 | Test the server in a multi-process environment
72 | '''
73 | server_num = 2
74 | servers = []
75 | for i in range(server_num):
76 | server = Server(dict(
77 | team_num=1,
78 | player_num_per_team=1,
79 | match_time=60*1,
80 | ))
81 | server.reset()
82 | servers.append(server)
83 |
84 | def run(server_index):
85 | for i in range(server_num):
86 | actions = {player_name: [random.uniform(-1, 1), random.uniform(-1, 1), -1] \
87 | for player_name in servers[server_index].get_player_names()}
88 | done = servers[server_index].step(actions=actions)
89 | global_state, players_obs, info = servers[server_index].obs()
90 | logging.debug('{} {} {}'.format(server_index, i, global_state))
91 | logging.debug('{} start close'.format(server_index))
92 | logging.debug('{} finish'.format(server_index))
93 |
94 | ps = []
95 | for i in range(server_num):
96 | p = mp.Process(target=run, args=(i,), daemon=True)
97 | ps.append(p)
98 |
99 | for p in ps:
100 | p.start()
101 |
102 | for p in ps:
103 | p.join()
104 |
105 |
--------------------------------------------------------------------------------
/gobigger/server/tests/test_server_sp.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 | import uuid
4 | from pygame.math import Vector2
5 | import pygame
6 | import random
7 | import numpy as np
8 | import cv2
9 | import multiprocessing as mp
10 |
11 | from gobigger.utils import Border
12 | from gobigger.server import ServerSP
13 | from gobigger.render import RealtimeRender, RealtimePartialRender, EnvRender
14 |
15 | logging.basicConfig(level=logging.DEBUG)
16 |
17 |
18 | @pytest.mark.unittest
19 | class TestServerSP:
20 |
21 | def test_init(self):
22 | server = ServerSP()
23 | assert True
24 |
25 | def test_spawn_balls(self):
26 | server = ServerSP()
27 | server.reset()
28 |
29 | def test_step_control_random(self):
30 | server = ServerSP()
31 | server.reset()
32 | obs = server.obs()
33 | fps_set = 20
34 | clock = pygame.time.Clock()
35 | render = RealtimePartialRender()
36 | for i in range(10):
37 | actions = {player_name: {ball[-1]: [random.uniform(-1, 1), random.uniform(-1, 1), -1]\
38 | for ball in obs[1][0]['overlap']['clone']} \
39 | for player_name in server.get_player_names()}
40 | done = server.step(actions=actions)
41 | obs = server.obs()
42 | render.fill(obs[0], obs[1][0], player_num_per_team=1, fps=10)
43 | render.show()
44 | clock.tick(fps_set)
45 | server.close()
46 |
47 | def test_obs(self):
48 | server = ServerSP()
49 | server.reset()
50 | obs = server.obs()
51 | for i in range(10):
52 | actions = {player_name: {ball[-1]: [random.uniform(-1, 1), random.uniform(-1, 1), -1]\
53 | for ball in obs[1][0]['overlap']['clone']} \
54 | for player_name in server.get_player_names()}
55 | done = server.step(actions=actions)
56 | obs = server.obs()
57 | logging.debug(obs[0])
58 |
59 | def test_obs_multi_player(self):
60 | server = ServerSP(dict(
61 | team_num=1,
62 | player_num_per_team=2,
63 | ))
64 | server.reset()
65 | obs = server.obs()
66 | for i in range(10):
67 | actions = {player_name: {ball[-1]: [random.uniform(-1, 1), random.uniform(-1, 1), -1]\
68 | for ball in obs[1][0]['overlap']['clone']} \
69 | for player_name in server.get_player_names()}
70 | done = server.step(actions=actions)
71 | obs = server.obs()
72 | logging.debug(obs[0])
73 |
--------------------------------------------------------------------------------
/gobigger/utils/__init__.py:
--------------------------------------------------------------------------------
1 | from .structures import format_vector, add_score, save_screen_data_to_img, Border, QuadNode
2 | from .collision_detection import create_collision_detection
3 | from .config_utils import deep_merge_dicts
4 | from .tool import *
5 | from .colors import *
6 | from .obs_utils import PlayerStatesUtil, PlayerStatesSPUtil
7 | # from .playback_utils import PlaybackUtil
8 | from .generator_utils import SequenceGenerator
9 |
--------------------------------------------------------------------------------
/gobigger/utils/colors.py:
--------------------------------------------------------------------------------
1 | FOOD_COLOR_GRAYSCALE = (1)
2 | THORNS_COLOR_GRAYSCALE = (2)
3 | SPORE_COLOR_GRAYSCALE = (3)
4 | PLAYER_COLORS_GRAYSCALE = [(i) for i in range(4, 100)]
5 | BACKGROUND_GRAYSCALE = (255)
6 |
7 | FOOD_COLOR = (253, 246, 227)
8 | THORNS_COLOR = (107, 194, 12)
9 | SPORE_COLOR = (255, 153, 18)
10 | PLAYER_COLORS = [
11 | [
12 | (38,139,210), # blue
13 | ],
14 | [
15 | (254,223,2), # yellow
16 | ],
17 | [
18 | (177,109,202), # purple
19 | ],
20 | [
21 | (42,161,152), # green
22 | ],
23 | [
24 | (203,75,22), # orange
25 | ],
26 | [
27 | (195,206,231), # gray
28 | ],
29 | [
30 | (128,128,128), # gray2
31 | ],
32 | [
33 | (128,0,0), # maroon
34 | ],
35 | [
36 | (128,128,0), # olive
37 | ],
38 | [
39 | (0,128,0), # green2
40 | ],
41 | [
42 | (128,0,128), # purple2
43 | ],
44 | [
45 | (0,128,128), # teal
46 | ],
47 | [
48 | (0,0,128), # navy
49 | ],
50 | [
51 | (255,127,80), # coral
52 | ],
53 | [
54 | (255,215,0), # goal
55 | ],
56 | [
57 | (154,205,50), # yellow green
58 | ],
59 | ]
60 | BACKGROUND = (0, 43, 54)
61 |
62 | GRAY = (220, 220, 220)
63 | BLACK = (0, 0, 0)
64 | RED = (255, 0, 0)
65 | YELLOW = (255, 153, 18)
66 | GREEN = (0, 255, 0)
67 | PURPLE = (160, 32, 240)
68 | WHITE = (255,255,255)
69 |
--------------------------------------------------------------------------------
/gobigger/utils/config_utils.py:
--------------------------------------------------------------------------------
1 | import copy
2 | from typing import List, Optional
3 |
4 |
5 | def deep_merge_dicts(original: dict, new_dict: dict) -> dict:
6 | """
7 | Overview:
8 | Merge two dicts by calling ``deep_update``
9 | Arguments:
10 | - original (:obj:`dict`): Dict 1.
11 | - new_dict (:obj:`dict`): Dict 2.
12 | Returns:
13 | - merged_dict (:obj:`dict`): A new dict that is d1 and d2 deeply merged.
14 | """
15 | original = original or {}
16 | new_dict = new_dict or {}
17 | merged = copy.deepcopy(original)
18 | if new_dict: # if new_dict is neither empty dict nor None
19 | deep_update(merged, new_dict, True, [])
20 | return merged
21 |
22 |
23 | def deep_update(
24 | original: dict,
25 | new_dict: dict,
26 | new_keys_allowed: bool = False,
27 | whitelist: Optional[List[str]] = None,
28 | override_all_if_type_changes: Optional[List[str]] = None
29 | ):
30 | """
31 | Overview:
32 | Update original dict with values from new_dict recursively.
33 | Arguments:
34 | - original (:obj:`dict`): Dictionary with default values.
35 | - new_dict (:obj:`dict`): Dictionary with values to be updated
36 | - new_keys_allowed (:obj:`bool`): Whether new keys are allowed.
37 | - whitelist (:obj:`Optional[List[str]]`):
38 | List of keys that correspond to dict
39 | values where new subkeys can be introduced. This is only at the top
40 | level.
41 | - override_all_if_type_changes(:obj:`Optional[List[str]]`):
42 | List of top level
43 | keys with value=dict, for which we always simply override the
44 | entire value (:obj:`dict`), if the "type" key in that value dict changes.
45 |
46 | .. note::
47 |
48 | If new key is introduced in new_dict, then if new_keys_allowed is not
49 | True, an error will be thrown. Further, for sub-dicts, if the key is
50 | in the whitelist, then new subkeys can be introduced.
51 | """
52 | whitelist = whitelist or []
53 | override_all_if_type_changes = override_all_if_type_changes or []
54 |
55 | for k, value in new_dict.items():
56 | if k not in original and not new_keys_allowed:
57 | raise RuntimeError("Unknown config parameter `{}`. Base config have: {}.".format(k, original.keys()))
58 |
59 | # Both original value and new one are dicts.
60 | if isinstance(original.get(k), dict) and isinstance(value, dict):
61 | # Check old type vs old one. If different, override entire value.
62 | if k in override_all_if_type_changes and \
63 | "type" in value and "type" in original[k] and \
64 | value["type"] != original[k]["type"]:
65 | original[k] = value
66 | # Whitelisted key -> ok to add new subkeys.
67 | elif k in whitelist:
68 | deep_update(original[k], value, True)
69 | # Non-whitelisted key.
70 | else:
71 | deep_update(original[k], value, new_keys_allowed)
72 | # Original value not a dict OR new value not a dict:
73 | # Override entire value.
74 | else:
75 | original[k] = value
76 | return original
77 |
--------------------------------------------------------------------------------
/gobigger/utils/frame_utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import pickle
4 |
5 |
6 | def save_frame_info(self, save_frame_full_path, food_balls, thorns_balls, spore_balls, clone_balls):
7 | if save_frame_full_path != '':
8 | frame_info = {'food': [], 'thorns': [], 'spore': [], 'clone': []}
9 | # food
10 | for ball in food_balls:
11 | frame_info['food'].append([ball.position.x, ball.position.y, ball.radius])
12 | # thorns
13 | for ball in thorns_balls:
14 | frame_info['thorns'].append([ball.position.x, ball.position.y, ball.radius, ball.vel.x, ball.vel.y,
15 | ball.acc.x, ball.acc.y, ball.move_time, ball.moving])
16 | # spore
17 | for ball in spore_balls:
18 | frame_info['spore'].append([ball.position.x, ball.position.y, ball.radius, ball.direction.x,
19 | ball.direction.y, ball.vel.x, ball.vel.y,
20 | ball.acc.x, ball.acc.y, ball.move_time, ball.moving])
21 | # clone
22 | for ball in clone_balls:
23 | frame_info['clone'].append([ball.position.x, ball.position.y, ball.radius, ball.owner,
24 | ball.team_name, ball.vel.x, ball.vel.y, ball.acc.x, ball.acc.y,
25 | ball.vel_last.x, ball.vel_last.y, ball.acc_last.x, ball.acc_last.y,
26 | ball.direction.x, ball.direction.y, ball.last_given_acc.x,
27 | ball.last_given_acc.y, ball.age, ball.cooling_last, ball.stop_flag,
28 | ball.stop_time, ball.acc_stop.x, ball.acc_stop.y])
29 | with open(save_frame_full_path, 'wb') as f:
30 | pickle.dump(frame_info, f)
31 |
32 | def load_frame_info():
33 | custom_init_food = []
34 | custom_init_thorns = []
35 | custom_init_spore = []
36 | custom_init_clone = []
37 | if frame_path:
38 | with open(frame_path, 'rb') as f:
39 | data = pickle.load(f)
40 | custom_init_food = data['food']
41 | custom_init_thorns = data['thorns']
42 | custom_init_spore = data['spore']
43 | custom_init_clone = data['clone']
44 | return custom_init_food, custom_init_thorns, custom_init_spore, custom_init_clone
--------------------------------------------------------------------------------
/gobigger/utils/generator_utils.py:
--------------------------------------------------------------------------------
1 | class SequenceGenerator:
2 |
3 | def __init__(self, start=0):
4 | self.start = 0
5 |
6 | def get(self):
7 | ret = self.start
8 | self.start += 1
9 | return ret
10 |
11 |
--------------------------------------------------------------------------------
/gobigger/utils/test/test_config_utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 | import pytest
4 | from easydict import EasyDict
5 |
6 | from gobigger.utils import deep_merge_dicts
7 |
8 | logging.basicConfig(level=logging.DEBUG)
9 |
10 |
11 | def test_deep_merge_dicts():
12 | a = EasyDict(dict(
13 | name='aaa',
14 | content=dict(
15 | team_num=4,
16 | map_width=1000
17 | )
18 | ))
19 | b = EasyDict(dict(
20 | name='bbb',
21 | content=dict(
22 | map_width=2000
23 | )
24 | ))
25 | c = deep_merge_dicts(a, b)
26 | assert c.name == 'bbb'
27 | assert c.content.map_width == 2000
28 | assert c.content.team_num == 4
29 |
--------------------------------------------------------------------------------
/gobigger/utils/test/test_sequence_generator.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 | import pytest
4 | import uuid
5 | from pygame.math import Vector2
6 | import pygame
7 | import random
8 | import numpy as np
9 | import cv2
10 |
11 | from gobigger.utils import SequenceGenerator
12 |
13 | logging.basicConfig(level=logging.DEBUG)
14 |
15 |
16 | def test_sequence_generator():
17 | class Temp:
18 | def __init__(self, sequence_generator=None):
19 | self.sequence_generator = sequence_generator
20 | def generate(self):
21 | return self.sequence_generator.get()
22 | sequence_generator = SequenceGenerator(0)
23 | ts = [Temp(sequence_generator) for i in range(5)]
24 | for index, t in enumerate(ts):
25 | assert t.generate() == index
26 |
--------------------------------------------------------------------------------
/gobigger/utils/test/test_structures.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 | import pytest
4 | import uuid
5 | from pygame.math import Vector2
6 | import pygame
7 | import random
8 | import numpy as np
9 | import cv2
10 |
11 | from gobigger.balls import BaseBall
12 | from gobigger.utils import format_vector, add_score, save_screen_data_to_img, Border, QuadNode
13 |
14 | logging.basicConfig(level=logging.DEBUG)
15 |
16 |
17 | def test_format_vector():
18 | v = Vector2(6, 8)
19 | norm_max = 5
20 | v_format = format_vector(v, norm_max=norm_max)
21 | assert v_format.x == 3
22 | assert v_format.y == 4
23 |
24 |
25 | def test_add_score():
26 | score_old = 10
27 | score_add = 20
28 | score_new = add_score(score_old, score_add)
29 | assert score_new == 30
30 |
31 |
32 | def test_save_screen_data_to_img():
33 | screen_data = (np.random.rand(100, 100, 3) * 255).astype(np.uint8)
34 | img_path = './temp.jpg'
35 | save_screen_data_to_img(screen_data, img_path=None)
36 | assert True
37 |
38 |
39 | @pytest.mark.unittest
40 | class TestBorder:
41 |
42 | def test_init(self):
43 | border = Border(0, 0, 1000, 1000)
44 | assert border.minx == 0
45 | assert border.miny == 0
46 | assert border.maxx == 1000
47 | assert border.maxy == 1000
48 | assert border.width == 1000
49 | assert border.height == 1000
50 |
51 | def test_contains(self):
52 | border = Border(0, 0, 1000, 1000)
53 | assert border.contains(position=Vector2(300, 300))
54 | assert not border.contains(position=Vector2(1300, 300))
55 |
56 | def test_sample(self):
57 | border = Border(0, 0, 1000, 1000)
58 | s = border.sample()
59 | assert border.contains(s)
60 |
61 | def test_get_joint(self):
62 | border = Border(0, 0, 1000, 1000)
63 | border_new = border.get_joint(border=Border(300, 300, 600, 600))
64 | assert border_new.minx == 300
65 | assert border_new.maxx == 300
66 | assert border_new.miny == 600
67 | assert border_new.maxy == 600
68 |
69 |
70 | @pytest.mark.unittest
71 | class TestQuadNode:
72 |
73 | def test_init(self):
74 | border = Border(0, 0, 1000, 1000)
75 | quad_node = QuadNode(border)
76 | assert quad_node.max_depth == 32
77 |
78 | def test_get_quad(self):
79 | border = Border(0, 0, 1000, 1000)
80 | quad_node = QuadNode(border)
81 | node = BaseBall('0', position=border.sample(), border=border, score=1)
82 | assert isinstance(quad_node.get_quad(node=node), int)
83 |
84 | def test_insert(self):
85 | border = Border(0, 0, 1000, 1000)
86 | quad_node = QuadNode(border)
87 | node = BaseBall('0', position=border.sample(), border=border, score=1)
88 | quad_node.insert(node=node)
89 |
90 | def test_find(self):
91 | border = Border(0, 0, 1000, 1000)
92 | quad_node = QuadNode(border)
93 | node = BaseBall('0', position=border.sample(), border=border, score=1)
94 | quad_node.find(border)
95 |
96 | def test_clear(self):
97 | border = Border(0, 0, 1000, 1000)
98 | quad_node = QuadNode(border)
99 | node = BaseBall('0', position=border.sample(), border=border, score=1)
100 | quad_node.clear()
101 |
102 | def test_remove(self):
103 | border = Border(0, 0, 1000, 1000)
104 | quad_node = QuadNode(border)
105 | node = BaseBall('0', position=border.sample(), border=border, score=1)
106 | quad_node.remove(node=node)
107 |
--------------------------------------------------------------------------------
/gobigger/utils/tool.py:
--------------------------------------------------------------------------------
1 | import math
2 | import random
3 | import numpy as np
4 |
5 |
6 | def chunks(arr, m):
7 | n = int(math.ceil(len(arr) / float(m)))
8 | return [arr[i:i + n] for i in range(0, len(arr), n)]
9 |
10 | def get_probability(src,arr):
11 | diff = [abs(i-src)+0.001 for i in arr]
12 | return [1/i if 1/i<1 else 1 for i in diff]
13 |
14 | def norm(arr):
15 | return [i/sum(arr) for i in arr]
16 |
17 | def to_aliased_circle(position, radius, cut_num=8, decrease=1):
18 | point_list = []
19 | radius_decrease = radius - decrease
20 | assert radius_decrease > 0
21 | piece_angle = math.pi / (cut_num)
22 | for i in range(cut_num*2):
23 | angle = piece_angle * i
24 | if i % 2 == 0:
25 | point_list.append([position.x + radius * math.cos(angle), position.y + radius * math.sin(angle)])
26 | else:
27 | point_list.append([position.x + radius_decrease * math.cos(angle), position.y + radius_decrease * math.sin(angle)])
28 | return point_list
29 |
30 | def to_arrow(position, radius, direction, out=1.2):
31 | x0, y0 = position.x, position.y
32 | x, y = direction.x, direction.y
33 | point_list = [
34 | [x0 + out * radius * x, y0 + out * radius * y],
35 | [x0 - math.sqrt(2)/2 * radius * (y - x), y0 + math.sqrt(2)/2 * radius * (x + y)],
36 | [x0 + math.sqrt(2)/2 * radius * (x + y), y0 + math.sqrt(2)/2 * radius * (y - x)],
37 | ]
38 | return point_list
39 |
--------------------------------------------------------------------------------
/practice/README.md:
--------------------------------------------------------------------------------
1 | ## Practice
2 | We offer multiple gameplay modes for players to enjoy, including development, battle (Bot and AI) and spectator mode. Our platform supports both single-player and two-player matches. Welcome to explore and enjoy the experience.
3 |
4 | ### Download Weight
5 | ```bash
6 | wget https://opendilab.net/download/GoBigger/solo_agent.pth.tar
7 | wget https://opendilab.net/download/GoBigger/cooperative_agent.pth.tar
8 | ```
9 | ### Quick Start
10 |
11 | #### Installation
12 | ```bash
13 | git clone https://github.com/opendilab/GoBigger.git
14 | pip install -e .
15 | ```
16 |
17 | #### Usage
18 | ```bash
19 | python battle.py --mode single --map farm # Single-player development
20 | python battle.py --mode single --map vsbot # Single-player vs. Bot
21 | python battle.py --mode single --map vsai # Single-player vs. Single-AI
22 | python battle.py --mode team --map farm # Two-player development
23 | python battle.py --mode team --map vsbot # Two-player vs. Bot
24 | python battle.py --mode team --map vsai # Two-player vs. Team-AI
25 | python battle.py --mode watch # Spectator mode: Team-AI vs. Team-AI
26 | ```
27 |
--------------------------------------------------------------------------------
/practice/cooperative_agent/agent.py:
--------------------------------------------------------------------------------
1 | import torch
2 | from practice.tools.util import default_collate_with_dim
3 | from practice.tools.features import Features
4 | from practice.cooperative_agent.model import Model
5 | from copy import deepcopy
6 | from easydict import EasyDict
7 | import torch
8 |
9 | class AIAgent:
10 |
11 | def __init__(self, team_name, player_names):
12 | cfg = EasyDict({
13 | 'team_name': team_name,
14 | 'player_names': player_names,
15 | 'env': {
16 | 'name': 'gobigger',
17 | 'player_num_per_team': 2,
18 | 'team_num': 2,
19 | 'step_mul': 8
20 | },
21 | 'agent': {
22 | 'player_id': None,
23 | 'game_player_id': None,
24 | 'features': {}
25 | },
26 | 'checkpoint_path': 'PATH/MODEL_NAME.pth.tar'
27 | })
28 | self.agents = {}
29 | for player_name in player_names:
30 | cfg_cp = deepcopy(cfg)
31 | cfg_cp.agent.player_id = player_name
32 | cfg_cp.agent.game_player_id = player_name
33 | agent = Agent(cfg_cp)
34 | agent.reset()
35 | agent.model.load_state_dict(torch.load(cfg.checkpoint_path, map_location='cpu')['model'], strict=False)
36 | self.agents[player_name] = agent
37 |
38 | def get_actions(self, obs):
39 | global_state, player_states = obs
40 | actions = {}
41 | for player_name, agent in self.agents.items():
42 | action = agent.step([global_state, {player_name: player_states[player_name]}])
43 | actions.update(action)
44 | return actions
45 |
46 | class Agent:
47 |
48 | def __init__(self, cfg,):
49 | self.whole_cfg = cfg
50 | self.player_num = self.whole_cfg.env.player_num_per_team
51 | self.team_num = self.whole_cfg.env.team_num
52 | self.game_player_id = self.whole_cfg.agent.game_player_id # start from 0
53 | self.game_team_id = self.game_player_id // self.player_num # start from 0
54 | self.player_id = self.whole_cfg.agent.player_id
55 | self.features = Features(self.whole_cfg)
56 | self.eval_padding = self.whole_cfg.agent.get('eval_padding', False)
57 | self.use_action_mask = self.whole_cfg.agent.get('use_action_mask', False)
58 | self.model = Model(self.whole_cfg)
59 |
60 | def reset(self):
61 | self.last_action_type = self.features.direction_num * 2
62 |
63 | def preprocess(self, obs):
64 | self.last_player_score = obs[1][self.game_player_id]['score']
65 | if self.use_action_mask:
66 | can_eject = obs[1][self.game_player_id]['can_eject']
67 | can_split = obs[1][self.game_player_id]['can_split']
68 | action_mask = self.features.generate_action_mask(can_eject=can_eject,can_split=can_split)
69 | else:
70 | action_mask = self.features.generate_action_mask(can_eject=True,can_split=True)
71 | obs = self.features.transform_obs(obs, game_player_id=self.game_player_id,
72 | last_action_type=self.last_action_type,padding=self.eval_padding)
73 | obs = default_collate_with_dim([obs])
74 |
75 | obs['action_mask'] = action_mask.unsqueeze(0)
76 | return obs
77 |
78 | def step(self, obs):
79 | self.raw_obs = obs
80 | obs = self.preprocess(obs)
81 | self.model_input = obs
82 | with torch.no_grad():
83 | self.model_output = self.model.compute_action(self.model_input)
84 | actions = self.postprocess(self.model_output['action'].detach().numpy())
85 | return actions
86 |
87 | def postprocess(self, model_actions):
88 | actions = {}
89 | actions[self.game_player_id] = self.features.transform_action(model_actions[0])
90 | self.last_action_type = model_actions[0].item()
91 | return actions
92 |
--------------------------------------------------------------------------------
/practice/cooperative_agent/model.py:
--------------------------------------------------------------------------------
1 | import os
2 | import torch
3 | import torch.nn as nn
4 | from ..tools.util import read_config, deep_merge_dicts
5 | from ..tools.encoder import Encoder
6 | from ..tools.head import PolicyHead, ValueHead
7 |
8 | default_config = read_config(os.path.join(os.path.dirname(__file__), 'default_model_config.yaml'))
9 |
10 | class Model(nn.Module):
11 | def __init__(self, cfg={}, use_value_network=False):
12 | super(Model, self).__init__()
13 | self.whole_cfg = deep_merge_dicts(default_config, cfg)
14 | self.model_cfg = self.whole_cfg.model
15 | self.use_value_network = use_value_network
16 | self.encoder = Encoder(self.whole_cfg)
17 | self.policy_head = PolicyHead(self.whole_cfg)
18 | self.temperature = self.whole_cfg.agent.get('temperature', 1)
19 |
20 | # used in rl_eval actor
21 | def compute_action(self, obs, ):
22 | action_mask = obs.pop('action_mask',None)
23 | embedding = self.encoder(obs, )
24 | logit = self.policy_head(embedding, temperature=self.temperature)
25 | if action_mask is not None:
26 | logit.masked_fill_(mask=action_mask,value=-1e9)
27 | dist = torch.distributions.Categorical(logits=logit)
28 | action = dist.sample()
29 | return {'action': action, 'logit': logit}
--------------------------------------------------------------------------------
/practice/solo_agent/default_model_config.yaml:
--------------------------------------------------------------------------------
1 | var1: &VIEW_BINARY_NUM 8
2 | var2: &ABS_VIEW_BINARY_NUM 7
3 | agent:
4 | enable_baselines: [ 'score', 'spore','team_spore', 'clone','team_clone','opponent','team_opponent' ]
5 | features:
6 | max_ball_num: 64
7 | max_food_num: 256
8 | max_spore_num: 128
9 | direction_num: 12
10 | spatial_x: 64
11 | spatial_y: 64
12 | model:
13 | scalar_encoder:
14 | modules:
15 | view_x:
16 | arc: sign_binary
17 | num_embeddings: *ABS_VIEW_BINARY_NUM
18 | embedding_dim: 8
19 | view_y:
20 | arc: sign_binary
21 | num_embeddings: *ABS_VIEW_BINARY_NUM
22 | embedding_dim: 8
23 | view_width:
24 | arc: binary
25 | num_embeddings: *ABS_VIEW_BINARY_NUM
26 | embedding_dim: 8
27 | # view_height:
28 | # arc: binary
29 | # num_embeddings: *ABS_VIEW_BINARY_NUM
30 | # embedding_dim: 8
31 | score:
32 | arc: one_hot
33 | num_embeddings: 10
34 | embedding_dim: 8
35 | team_score:
36 | arc: one_hot
37 | num_embeddings: 10
38 | embedding_dim: 8
39 | rank:
40 | arc: one_hot
41 | num_embeddings: 4
42 | embedding_dim: 8
43 | time:
44 | arc: time
45 | embedding_dim: 8
46 | last_action_type:
47 | arc: one_hot
48 | num_embeddings: 27 # direction_num * 2 + 3
49 | input_dim: 80
50 | hidden_dim: 64
51 | layer_num: 2
52 | norm_type: 'none'
53 | output_dim: 32
54 | activation: 'relu'
55 | team_encoder:
56 | modules:
57 | alliance:
58 | arc: one_hot
59 | num_embeddings: 2
60 | view_x:
61 | arc: sign_binary
62 | num_embeddings: *ABS_VIEW_BINARY_NUM
63 | view_y:
64 | arc: sign_binary
65 | num_embeddings: *ABS_VIEW_BINARY_NUM
66 | # view_width:
67 | # arc: binary
68 | # num_embeddings: *ABS_VIEW_BINARY_NUM
69 | # embedding_dim: 12
70 | # view_height:
71 | # arc: binary
72 | # num_embeddings: *ABS_VIEW_BINARY_NUM
73 | # embedding_dim: 12
74 | # score:
75 | # arc: one_hot
76 | # num_embeddings: 10
77 | # embedding_dim: 12
78 | # team_score:
79 | # arc: one_hot
80 | # num_embeddings: 10
81 | # embedding_dim: 12
82 | # team_rank:
83 | # arc: one_hot
84 | # num_embeddings: 10
85 | # embedding_dim: 12
86 | embedding_dim: 16
87 | encoder:
88 | input_dim: 16
89 | hidden_dim: 32
90 | layer_num: 2
91 | activation: 'relu'
92 | norm_type: 'LN'
93 | transformer:
94 | head_num: 4
95 | ffn_size: 32
96 | layer_num: 2
97 | activation: 'relu'
98 | variant: 'postnorm'
99 | output:
100 | output_dim: 16
101 | activation: 'relu'
102 | norm_type: 'LN'
103 | ball_encoder:
104 | modules:
105 | alliance:
106 | arc: one_hot
107 | num_embeddings: 4
108 | score:
109 | arc: one_hot
110 | num_embeddings: 50
111 | radius:
112 | arc: unsqueeze
113 | # score_ratio:
114 | # arc: one_hot
115 | # num_embeddings: 50
116 | rank:
117 | arc: one_hot
118 | num_embeddings: 5
119 | x:
120 | arc: sign_binary
121 | num_embeddings: *VIEW_BINARY_NUM
122 | embedding_dim: 8
123 | y:
124 | arc: sign_binary
125 | num_embeddings: *VIEW_BINARY_NUM
126 | embedding_dim: 8
127 | next_x:
128 | arc: sign_binary
129 | num_embeddings: *VIEW_BINARY_NUM
130 | embedding_dim: 8
131 | next_y:
132 | arc: sign_binary
133 | num_embeddings: *VIEW_BINARY_NUM
134 | embedding_dim: 8
135 | embedding_dim: 64
136 | encoder:
137 | input_dim: 92
138 | hidden_dim: 128
139 | layer_num: 2
140 | activation: 'relu'
141 | norm_type: 'LN'
142 | transformer:
143 | head_num: 4
144 | ffn_size: 64
145 | layer_num: 3
146 | activation: 'relu'
147 | variant: 'postnorm'
148 | output:
149 | output_dim: 64
150 | activation: 'relu'
151 | norm_type: 'LN'
152 | spatial_encoder:
153 | scatter:
154 | input_dim: 64
155 | output_dim: 16
156 | scatter_type: add
157 | activation: 'relu'
158 | norm_type: 'LN'
159 | resnet:
160 | project_dim: 12
161 | down_channels: [32, 32, 16 ]
162 | activation: 'relu'
163 | norm_type: 'LN'
164 | output:
165 | output_dim: 64
166 | activation: 'relu'
167 | norm_type: 'LN'
168 | policy:
169 | embedding_dim: 64
170 | project:
171 | input_dim: 176 # scalar + team + ball + spatial
172 | activation: 'relu'
173 | norm_type: 'LN'
174 | resnet:
175 | activation: 'relu'
176 | norm_type: 'LN'
177 | res_num: 3
178 | value:
179 | embedding_dim: 64
180 | project:
181 | input_dim: 352 # scalar + team + ball + spatial
182 | activation: 'relu'
183 | norm_type: 'LN'
184 | resnet:
185 | activation: 'relu'
186 | norm_type: 'LN'
187 | res_num: 3
--------------------------------------------------------------------------------
/practice/test_ai.py:
--------------------------------------------------------------------------------
1 | from gobigger.envs import create_env
2 | from cooperative_agent.agent import AIAgent as AI
3 |
4 | env = create_env('st_t2p2')
5 | obs = env.reset()
6 |
7 | agent1 = AI(team_name=0, player_names=[0,1])
8 | agent2 = AI(team_name=1, player_names=[2,3])
9 |
10 | for i in range(1000):
11 | actions1 = agent1.get_actions(obs)
12 | actions2 = agent2.get_actions(obs)
13 | actions1.update(actions2)
14 | obs, rew, done, info = env.step(actions1)
15 | print('[{}] leaderboard={}'.format(i, obs[0]['leaderboard']))
16 | if done:
17 | print('finish game!')
18 | break
19 | env.close()
--------------------------------------------------------------------------------
/practice/tools/head.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 |
3 | from .network.nn_module import fc_block
4 | from .network.res_block import ResFCBlock
5 |
6 |
7 | class PolicyHead(nn.Module):
8 | def __init__(self, cfg):
9 | super(PolicyHead, self).__init__()
10 | self.whole_cfg = cfg
11 | self.cfg = self.whole_cfg.model.policy
12 |
13 | self.embedding_dim = self.cfg.embedding_dim
14 | self.project_cfg = self.cfg.project
15 | self.project = fc_block(in_channels=self.project_cfg.input_dim,
16 | out_channels=self.embedding_dim,
17 | activation= self.project_cfg.activation,
18 | norm_type=self.project_cfg.norm_type)
19 |
20 | self.resnet_cfg = self.cfg.resnet
21 | blocks = [ResFCBlock(in_channels=self.embedding_dim,
22 | activation=self.resnet_cfg.activation,
23 | norm_type=self.resnet_cfg.norm_type)
24 | for _ in range(self.resnet_cfg.res_num)]
25 | self.resnet = nn.Sequential(*blocks)
26 |
27 | self.direction_num = self.whole_cfg.agent.features.get('direction_num', 12)
28 | self.action_num = 2 * self.direction_num + 3
29 | self.output_layer = fc_block(in_channels=self.embedding_dim,
30 | out_channels=self.action_num,
31 | norm_type=None,
32 | activation=None)
33 |
34 | def forward(self, x, temperature=1):
35 | x = self.project(x)
36 | x = self.resnet(x)
37 | logit = self.output_layer(x)
38 | logit /= temperature
39 | return logit
40 |
41 |
42 | class ValueHead(nn.Module):
43 | def __init__(self, cfg):
44 | super(ValueHead, self).__init__()
45 | self.whole_cfg = cfg
46 | self.cfg = self.whole_cfg.model.value
47 |
48 | self.embedding_dim = self.cfg.embedding_dim
49 | self.project_cfg = self.cfg.project
50 | self.project = fc_block(in_channels=self.project_cfg.input_dim,
51 | out_channels=self.embedding_dim,
52 | activation= self.project_cfg.activation,
53 | norm_type=self.project_cfg.norm_type)
54 |
55 | self.resnet_cfg = self.cfg.resnet
56 | blocks = [ResFCBlock(in_channels=self.embedding_dim,
57 | activation=self.resnet_cfg.activation,
58 | norm_type=self.resnet_cfg.norm_type)
59 | for _ in range(self.resnet_cfg.res_num)]
60 | self.resnet = nn.Sequential(*blocks)
61 |
62 | self.output_layer = fc_block(in_channels=self.embedding_dim,
63 | out_channels=1,
64 | norm_type=None,
65 | activation=None)
66 | def forward(self, x):
67 | x = self.project(x)
68 | x = self.resnet(x)
69 | x = self.output_layer(x)
70 | x = x.squeeze(1)
71 | return x
--------------------------------------------------------------------------------
/practice/tools/network/__init__.py:
--------------------------------------------------------------------------------
1 | from .activation import build_activation
2 | from .res_block import ResBlock, ResFCBlock,ResFCBlock2
3 | from .nn_module import fc_block, fc_block2, conv2d_block, MLP
4 | from .normalization import build_normalization
5 | from .rnn import get_lstm, sequence_mask
6 | from .soft_argmax import SoftArgmax
7 | from .transformer import Transformer
8 | from .scatter_connection import ScatterConnection
9 |
--------------------------------------------------------------------------------
/practice/tools/network/activation.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright 2020 Sensetime X-lab. All Rights Reserved
3 |
4 | Main Function:
5 | 1. build activation: you can use build_activation to build relu or glu
6 | """
7 | import torch
8 | import torch.nn as nn
9 |
10 |
11 | class GLU(nn.Module):
12 | r"""
13 | Overview:
14 | Gating Linear Unit.
15 | This class does a thing like this:
16 |
17 | .. code:: python
18 |
19 | # Inputs: input, context, output_size
20 | # The gate value is a learnt function of the input.
21 | gate = sigmoid(linear(input.size)(context))
22 | # Gate the input and return an output of desired size.
23 | gated_input = gate * input
24 | output = linear(output_size)(gated_input)
25 | return output
26 | Interfaces:
27 | forward
28 |
29 | .. tip::
30 |
31 | This module also supports 2D convolution, in which case, the input and context must have the same shape.
32 | """
33 |
34 | def __init__(self, input_dim: int, output_dim: int, context_dim: int, input_type: str = 'fc') -> None:
35 | r"""
36 | Overview:
37 | Init GLU
38 | Arguments:
39 | - input_dim (:obj:`int`): the input dimension
40 | - output_dim (:obj:`int`): the output dimension
41 | - context_dim (:obj:`int`): the context dimension
42 | - input_type (:obj:`str`): the type of input, now support ['fc', 'conv2d']
43 | """
44 | super(GLU, self).__init__()
45 | assert (input_type in ['fc', 'conv2d'])
46 | if input_type == 'fc':
47 | self.layer1 = nn.Linear(context_dim, input_dim)
48 | self.layer2 = nn.Linear(input_dim, output_dim)
49 | elif input_type == 'conv2d':
50 | self.layer1 = nn.Conv2d(context_dim, input_dim, 1, 1, 0)
51 | self.layer2 = nn.Conv2d(input_dim, output_dim, 1, 1, 0)
52 |
53 | def forward(self, x: torch.Tensor, context: torch.Tensor) -> torch.Tensor:
54 | r"""
55 | Overview:
56 | Return GLU computed tensor
57 | Arguments:
58 | - x (:obj:`torch.Tensor`) : the input tensor
59 | - context (:obj:`torch.Tensor`) : the context tensor
60 | Returns:
61 | - x (:obj:`torch.Tensor`): the computed tensor
62 | """
63 | gate = self.layer1(context)
64 | gate = torch.sigmoid(gate)
65 | x = gate * x
66 | x = self.layer2(x)
67 | return x
68 |
69 | class Swish(nn.Module):
70 |
71 | def __init__(self):
72 | super(Swish, self).__init__()
73 |
74 | def forward(self, x):
75 | x = x * torch.sigmoid(x)
76 | return x
77 |
78 | def build_activation(activation: str, inplace: bool = None) -> nn.Module:
79 | r"""
80 | Overview:
81 | Return the activation module according to the given type.
82 | Arguments:
83 | - actvation (:obj:`str`): the type of activation module, now supports ['relu', 'glu', 'prelu']
84 | - inplace (:obj:`bool`): can optionally do the operation in-place in relu. Default ``None``
85 | Returns:
86 | - act_func (:obj:`nn.module`): the corresponding activation module
87 | """
88 | if inplace is not None:
89 | assert activation == 'relu', 'inplace argument is not compatible with {}'.format(activation)
90 | else:
91 | inplace = True
92 | act_func = {'relu': nn.ReLU(inplace=inplace), 'glu': GLU, 'prelu': nn.PReLU(),'swish': Swish()}
93 | if activation in act_func.keys():
94 | return act_func[activation]
95 | else:
96 | raise KeyError("invalid key for activation: {}".format(activation))
97 |
--------------------------------------------------------------------------------
/practice/tools/network/normalization.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 | import torch.nn as nn
3 |
4 |
5 | def build_normalization(norm_type: str, dim: Optional[int] = None) -> nn.Module:
6 | r"""
7 | Overview:
8 | Build the corresponding normalization module
9 | Arguments:
10 | - norm_type (:obj:`str`): type of the normaliztion, now support ['BN', 'IN', 'SyncBN', 'AdaptiveIN']
11 | - dim (:obj:`int`): dimension of the normalization, when norm_type is in [BN, IN]
12 | Returns:
13 | - norm_func (:obj:`nn.Module`): the corresponding batch normalization function
14 |
15 | .. note::
16 | For beginers, you can refer to to learn more about batch normalization.
17 | """
18 | if dim is None:
19 | key = norm_type
20 | else:
21 | if norm_type in ['BN', 'IN', 'SyncBN']:
22 | key = norm_type + str(dim)
23 | elif norm_type in ['LN']:
24 | key = norm_type
25 | else:
26 | raise NotImplementedError("not support indicated dim when creates {}".format(norm_type))
27 | norm_func = {
28 | 'BN1': nn.BatchNorm1d,
29 | 'BN2': nn.BatchNorm2d,
30 | 'LN': nn.LayerNorm,
31 | 'IN2': nn.InstanceNorm2d,
32 | }
33 | if key in norm_func.keys():
34 | return norm_func[key]
35 | else:
36 | raise KeyError("invalid norm type: {}".format(key))
--------------------------------------------------------------------------------
/practice/tools/network/soft_argmax.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright 2020 Sensetime X-lab. All Rights Reserved
3 |
4 | Main Function:
5 | 1. SoftArgmax: a nn.Module that computes SoftArgmax
6 | """
7 | import torch
8 | import torch.nn as nn
9 | import torch.nn.functional as F
10 |
11 |
12 | class SoftArgmax(nn.Module):
13 | r"""
14 | Overview:
15 | a nn.Module that computes SoftArgmax
16 |
17 | Note:
18 | for more softargmax info, you can reference the wiki page
19 | or reference the lecture
20 |
21 |
22 | Interface:
23 | __init__, forward
24 | """
25 |
26 | def __init__(self):
27 | r"""
28 | Overview:
29 | initialize the SoftArgmax module
30 | """
31 | super(SoftArgmax, self).__init__()
32 |
33 | def forward(self, x):
34 | r"""
35 | Overview:
36 | soft-argmax for location regression
37 |
38 | Arguments:
39 | - x (:obj:`Tensor`): predict heat map
40 |
41 | Returns:
42 | - location (:obj:`Tensor`): predict location
43 |
44 | Shapes:
45 | - x (:obj:`Tensor`): :math:`(B, C, H, W)`, while B is the batch size,
46 | C is number of channels , H and W stands for height and width
47 | - location (:obj:`Tensor`): :math:`(B, 2)`, while B is the batch size
48 | """
49 | B, C, H, W = x.shape
50 | device, dtype = x.device, x.dtype
51 | # 1 channel
52 | assert (x.shape[1] == 1)
53 | h_kernel = torch.arange(0, H, device=device).to(dtype)
54 | h_kernel = h_kernel.view(1, 1, H, 1).repeat(1, 1, 1, W)
55 | w_kernel = torch.arange(0, W, device=device).to(dtype)
56 | w_kernel = w_kernel.view(1, 1, 1, W).repeat(1, 1, H, 1)
57 | x = F.softmax(x.view(B, C, -1), dim=-1).view(B, C, H, W)
58 | h = (x * h_kernel).sum(dim=[1, 2, 3])
59 | w = (x * w_kernel).sum(dim=[1, 2, 3])
60 | return torch.stack([h, w], dim=1)
61 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 |
5 | from setuptools import setup
6 |
7 |
8 | setup(
9 | name='gobigger',
10 | version='0.2.0',
11 | description='Go-Bigger: Multi-Agent Decision Intelligence Environment',
12 | author='OpenDILab',
13 | license='Apache License, Version 2.0',
14 | keywords='Go-Bigger DI',
15 | packages=[
16 | 'gobigger',
17 | 'gobigger.agents',
18 | 'gobigger.balls',
19 | 'gobigger.server',
20 | 'gobigger.utils',
21 | 'gobigger.managers',
22 | 'gobigger.players',
23 | 'gobigger.render',
24 | 'gobigger.envs',
25 | 'gobigger.bin',
26 | 'gobigger.configs',
27 | 'gobigger.playbacks',
28 | ],
29 | install_requires=[
30 | 'easydict',
31 | 'gym>=0.15.3', # pypy incompatible
32 | 'pygame>=2.0.0',
33 | 'pytest>=5.0.0',
34 | 'opencv-python',
35 | 'numpy>=1.10',
36 | 'numexpr',
37 | 'lz4',
38 | ]
39 | )
40 |
--------------------------------------------------------------------------------