diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 1823745..bfc8ffd 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -47,11 +47,12 @@ jobs: pytest: name: Run Pytest - runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, 'Bump version')" strategy: matrix: - python-version: ['3.10'] + python-version: [ '3.10' ] + os: [ 'ubuntu-latest', 'macos-latest' ] + runs-on: ${{ matrix.os }} steps: - name: actions/checkout uses: actions/checkout@v3 @@ -64,29 +65,64 @@ jobs: uses: eifinger/setup-rye@v4 with: enable-cache: true - cache-prefix: ubuntu-latest-rye-test-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} + cache-prefix: ${{ matrix.os }}-latest-rye-test-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} - name: actions/cache uses: actions/cache@v3 with: path: ${{ env.LOCAL_CACHE }} - key: ubuntu-latest-system-test-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} - restore-keys: ubuntu-latest-system-test - - name: Install test and package dependencies + key: ${{ matrix.os }}-latest-system-test-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} + restore-keys: ${{ matrix.os }}-latest-system-test-${{ matrix.python-version }} + + - name: Install Ubuntu test and package dependencies + if: ${{ matrix.os == 'ubuntu-latest' }} + run: | + set -x + sudo apt-get install -y xvfb libglu1-mesa + rye pin --relaxed cpython@${{ matrix.python-version }} + rye sync --all-features + ROM_PASSWORD=${{ secrets.ROM_PASSWORD }} rye run import-roms + + - name: Install MacOS test and package dependencies + if: ${{ matrix.os == 'macos-latest' }} run: | set -x - sudo apt-get install -y xvfb -y - sudo MUJOCO_PATH=/home/runner/.mujoco/ make install-envs + brew install --cask xquartz + brew install swig libzip qt5 capnp + chmod +x install-lua-macos.sh + sudo ./install-lua-macos.sh + # https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#adding-a-system-path + echo "/opt/X11/bin" >> $GITHUB_PATH + # https://github.com/ponty/PyVirtualDisplay/issues/42 + if [ ! -d /tmp/.X11-unix ]; then + mkdir /tmp/.X11-unix + fi + sudo chmod 1777 /tmp/.X11-unix + sudo chown root /tmp/.X11-unix + echo 'export PATH="/opt/homebrew/opt/qt@5/bin:$PATH"' >> ~/.zshrc + export SDKROOT=$(xcrun --sdk macosx --show-sdk-path) rye pin --relaxed cpython@${{ matrix.python-version }} rye sync --all-features - ROM_PASSWORD=${{ secrets.ROM_PASSWORD }} make import-roms + # rye sync --features=ray,box_2d,atari,classic-control,test + # Fix a bug in retro + sed -i '' 's/VERSION\.txt/VERSION/g' /Users/runner/work/plangym/plangym/.venv/lib/python3.10/site-packages/retro/__init__.py + echo "0.9.1" > /Users/runner/work/plangym/plangym/.venv/lib/python3.10/site-packages/retro/VERSION + ROM_PASSWORD=${{ secrets.ROM_PASSWORD }} rye run import-roms + + - name: Run Pytest on MacOS + if: ${{ matrix.os == 'macos-latest' }} + run: | + set -x + SKIP_RENDER=True rye run pytest tests/control tests/test_core.py - - name: Test with pytest + - name: Run code coverage on Ubuntu + if: ${{ matrix.os == 'ubuntu-latest' }} run: | set -x xvfb-run -s "-screen 0 1400x900x24" rye run codecov - name: Upload coverage report - if: ${{ matrix.python-version=='3.10' }} + # if: ${{ matrix.python-version == '3.10' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-latest' }} uses: codecov/codecov-action@v4 with: fail_ci_if_error: false # optional (default = false) diff --git a/.gitignore b/.gitignore index c6a5811..3f67a48 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ htmlcov/ .cache nosetests.xml coverage.xml +coverage_parallel.xml *.cover .hypothesis/ diff --git a/.readthedocs.yaml b/.readthedocs.yaml index fd7b8c9..7aa6eb8 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -15,6 +15,7 @@ build: # golang: "1.20" apt_packages: - build-essential + - libglu1-mesa - xvfb - clang - swig diff --git a/install-lua-macos.sh b/install-lua-macos.sh new file mode 100644 index 0000000..0d381ab --- /dev/null +++ b/install-lua-macos.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# This scripts installs Lua, LuaRocks, and some Lua libraries on macOS. +# The main purpose is to install Busted for testing Neovim plugins. +# After the installation, you will be able to run test using busted: +# busted --lua nlua spec/mytest_spec.lua + +################################################################################ +# Dependencies +################################################################################ + +xcode-select --install + +# Lua Directory: where Lua and Luarocks will be installed +# You can change installation location by changing this variable +LUA_DIR="$HOME/Developer/lua" + +mkdir -p $LUA_DIR + +################################################################################ +# Lua +################################################################################ + +# Download and Extract Lua Sources +cd /tmp +rm -rf lua-5.1.5.* +wget https://www.lua.org/ftp/lua-5.1.5.tar.gz +LUA_SHA='2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333' +shasum -a 256 lua-5.1.5.tar.gz | grep -q $LUA_SHA && echo "Hash matches" || echo "Hash don't match" +tar xvf lua-5.1.5.tar.gz +cd lua-5.1.5/ + +# Modify Makefile to set destination dir +sed -i '' "s#/usr/local#${LUA_DIR}/#g" Makefile + +# Compile and install Lua +make macosx +make test && make install + +# Export PATHs +export PATH="$PATH:$LUA_DIR/bin" +export LUA_CPATH="$LUA_DIR/lib/lua/5.1/?.so" +export LUA_PATH="$LUA_DIR/share/lua/5.1/?.lua;;" +export MANPATH="$LUA_DIR/share/man:$MANPATH" + +# Verify Lua Installation +which lua +echo "Expected Output:" +echo " ${LUA_DIR}/bin/lua" +lua -v +echo 'Expected Output:' +echo ' Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio' +file ${LUA_DIR}/bin/lua +echo "Expected Output (on Apple Silicon):" +echo " ${LUA_DIR}/bin/lua: Mach-O 64-bit executable arm64" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index c6eb118..2146178 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ classifiers = [ dependencies = [ "numpy", "pillow", + "fragile-gym", "opencv-python>=4.10.0.84", "pyglet==1.5.11", "pyvirtualdisplay>=3.0", @@ -32,14 +33,17 @@ dependencies = [ [project.optional-dependencies] atari = ["ale-py", "gymnasium[accept-rom-license,atari]>=0.29.1, == 0.*"] nes = [ - "gym[accept-rom-license] @ git+https://github.com/FragileTech/gym.git", - "nes-py @ git+https://github.com/FragileTech/nes-py", # Requires clang, build-essential - "gym-super-mario-bros==7.3.2", + "fragile-gym[accept-rom-license]", + "fragile-nes-py>=10.0.1", # Requires clang, build-essential + "fragile-gym-super-mario-bros", ] classic-control = ["gymnasium[classic_control]>=0.29.1, == 0.*", "pygame>=2.6.0"] ray = ["ray>=2.35.0"] -dm_control = ["dm-control>=1.0.22", "gym @ git+https://github.com/FragileTech/gym.git"] -retro = ["stable_retro"] +dm_control = ["mujoco>=3.2.2", "dm-control>=1.0.22"] +retro = [ + "stable-retro==0.9.2; sys_platform != 'darwin'", + "stable-retro==0.9.1; sys_platform == 'darwin'" +] jupyter = ["jupyterlab>=3.2.0"] box_2d = ["box2d-py==2.3.5"] test = [ @@ -81,13 +85,14 @@ path = "src/plangym/version.py" [tool.rye] dev-dependencies = ["ruff"] +#excluded-dependencies = ["gym"] universal = true [tool.rye.scripts] style = { chain = ["ruff check --fix-only --unsafe-fixes tests src", "ruff format tests src"] } check = { chain = ["ruff check --diff tests src", "ruff format --diff tests src"]} #,"mypy src tests" ] } test = { chain = ["test:doctest", "test:parallel", "test:singlecore"] } -codecov = { chain = ["codecov:parallel", "codecov:singlecore"] } +codecov = { chain = ["codecov:singlecore", "codecov:parallel"] } import-roms = { cmd = "python3 src/plangym/scripts/import_retro_roms.py" } "test:parallel" = { cmd = "pytest -n auto -s -o log_cli=true -o log_cli_level=info tests", env-file = ".multicore.env" } "test:singlecore" = { cmd = "pytest -s -o log_cli=true -o log_cli_level=info tests/control/test_classic_control.py", env-file = ".onecore.env" } diff --git a/requirements-dev.lock b/requirements-dev.lock index 72c9ece..f0cf65f 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -48,7 +48,7 @@ attrs==24.2.0 # via jupyter-cache # via referencing autorom==0.4.2 - # via gym + # via fragile-gym # via gymnasium autorom-accept-rom-license==0.6.1 # via autorom @@ -78,7 +78,7 @@ click==8.1.7 # via jupyter-cache # via ray cloudpickle==3.0.0 - # via gym + # via fragile-gym # via gymnasium colorama==0.4.6 ; sys_platform == 'win32' or platform_system == 'Windows' # via click @@ -121,7 +121,7 @@ exceptiongroup==1.2.2 ; python_full_version < '3.11' # via pytest execnet==2.1.1 # via pytest-xdist -executing==2.0.1 +executing==2.1.0 # via stack-data farama-notifications==0.0.4 # via gymnasium @@ -133,6 +133,13 @@ flogging==0.0.22 # via plangym fqdn==1.5.1 # via jsonschema +fragile-gym==1.21.1 + # via fragile-nes-py + # via plangym +fragile-gym-super-mario-bros==7.4.0 + # via plangym +fragile-nes-py==10.0.1 + # via plangym frozenlist==1.4.1 # via aiosignal # via ray @@ -143,11 +150,6 @@ glfw==2.7.0 # via mujoco greenlet==3.0.3 ; (python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64') # via sqlalchemy -gym @ git+https://github.com/FragileTech/gym.git@507d04d43a81602959e29eecfea51404b791a6d4 - # via nes-py - # via plangym -gym-super-mario-bros==7.3.2 - # via plangym gymnasium==0.29.1 # via plangym # via shimmy @@ -181,6 +183,7 @@ ipykernel==6.29.5 # via jupyterlab # via myst-nb ipython==8.27.0 + # via fragile-gym # via ipykernel # via myst-nb isoduration==20.11.0 @@ -265,6 +268,7 @@ msgpack==1.0.8 # via ray mujoco==3.2.2 # via dm-control + # via plangym myst-nb==1.1.1 # via plangym myst-parser==4.0.0 @@ -282,9 +286,6 @@ nbformat==5.10.4 # via myst-nb # via nbclient # via nbconvert -nes-py @ git+https://github.com/FragileTech/nes-py@1ca4da5a97ac8987874f214054ccbd08e102a899 - # via gym-super-mario-bros - # via plangym nest-asyncio==1.6.0 # via ipykernel notebook-shim==0.2.4 @@ -293,12 +294,12 @@ numpy==2.1.0 # via ale-py # via dm-control # via dm-env - # via gym + # via fragile-gym + # via fragile-nes-py # via gymnasium # via imageio # via labmaze # via mujoco - # via nes-py # via opencv-python # via plangym # via scipy @@ -360,7 +361,7 @@ pygame==2.6.0 # via gymnasium # via plangym pyglet==1.5.11 - # via nes-py + # via fragile-nes-py # via plangym # via stable-retro pygments==2.18.0 @@ -439,7 +440,7 @@ scipy==1.14.1 # via dm-control send2trash==1.8.3 # via jupyter-server -setuptools==74.0.0 +setuptools==74.1.0 # via dm-control # via jupyterlab # via labmaze @@ -474,7 +475,7 @@ sphinx==8.0.2 # via sphinx-togglebutton # via sphinxcontrib-bibtex # via sphinxext-opengraph -sphinx-autoapi==3.3.0 +sphinx-autoapi==3.3.1 # via plangym sphinx-autodoc2==0.5.0 # via plangym @@ -506,7 +507,7 @@ sphinxext-opengraph==0.9.1 # via plangym sqlalchemy==2.0.32 # via jupyter-cache -stable-retro==0.9.2 +stable-retro==0.9.1 # via plangym stack-data==0.6.3 # via ipython @@ -533,7 +534,7 @@ tornado==6.4.1 tqdm==4.66.5 # via autorom # via dm-control - # via nes-py + # via fragile-nes-py traitlets==5.14.3 # via comm # via ipykernel diff --git a/requirements.lock b/requirements.lock index 278bc03..9fb8762 100644 --- a/requirements.lock +++ b/requirements.lock @@ -48,7 +48,7 @@ attrs==24.2.0 # via jupyter-cache # via referencing autorom==0.4.2 - # via gym + # via fragile-gym # via gymnasium autorom-accept-rom-license==0.6.1 # via autorom @@ -78,7 +78,7 @@ click==8.1.7 # via jupyter-cache # via ray cloudpickle==3.0.0 - # via gym + # via fragile-gym # via gymnasium colorama==0.4.6 ; sys_platform == 'win32' or platform_system == 'Windows' # via click @@ -121,7 +121,7 @@ exceptiongroup==1.2.2 ; python_full_version < '3.11' # via pytest execnet==2.1.1 # via pytest-xdist -executing==2.0.1 +executing==2.1.0 # via stack-data farama-notifications==0.0.4 # via gymnasium @@ -133,6 +133,13 @@ flogging==0.0.22 # via plangym fqdn==1.5.1 # via jsonschema +fragile-gym==1.21.1 + # via fragile-nes-py + # via plangym +fragile-gym-super-mario-bros==7.4.0 + # via plangym +fragile-nes-py==10.0.1 + # via plangym frozenlist==1.4.1 # via aiosignal # via ray @@ -143,11 +150,6 @@ glfw==2.7.0 # via mujoco greenlet==3.0.3 ; (python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64') # via sqlalchemy -gym @ git+https://github.com/FragileTech/gym.git@507d04d43a81602959e29eecfea51404b791a6d4 - # via nes-py - # via plangym -gym-super-mario-bros==7.3.2 - # via plangym gymnasium==0.29.1 # via plangym # via shimmy @@ -181,6 +183,7 @@ ipykernel==6.29.5 # via jupyterlab # via myst-nb ipython==8.27.0 + # via fragile-gym # via ipykernel # via myst-nb isoduration==20.11.0 @@ -265,6 +268,7 @@ msgpack==1.0.8 # via ray mujoco==3.2.2 # via dm-control + # via plangym myst-nb==1.1.1 # via plangym myst-parser==4.0.0 @@ -282,9 +286,6 @@ nbformat==5.10.4 # via myst-nb # via nbclient # via nbconvert -nes-py @ git+https://github.com/FragileTech/nes-py@1ca4da5a97ac8987874f214054ccbd08e102a899 - # via gym-super-mario-bros - # via plangym nest-asyncio==1.6.0 # via ipykernel notebook-shim==0.2.4 @@ -293,12 +294,12 @@ numpy==2.1.0 # via ale-py # via dm-control # via dm-env - # via gym + # via fragile-gym + # via fragile-nes-py # via gymnasium # via imageio # via labmaze # via mujoco - # via nes-py # via opencv-python # via plangym # via scipy @@ -360,7 +361,7 @@ pygame==2.6.0 # via gymnasium # via plangym pyglet==1.5.11 - # via nes-py + # via fragile-nes-py # via plangym # via stable-retro pygments==2.18.0 @@ -438,7 +439,7 @@ scipy==1.14.1 # via dm-control send2trash==1.8.3 # via jupyter-server -setuptools==74.0.0 +setuptools==74.1.0 # via dm-control # via jupyterlab # via labmaze @@ -473,7 +474,7 @@ sphinx==8.0.2 # via sphinx-togglebutton # via sphinxcontrib-bibtex # via sphinxext-opengraph -sphinx-autoapi==3.3.0 +sphinx-autoapi==3.3.1 # via plangym sphinx-autodoc2==0.5.0 # via plangym @@ -505,7 +506,7 @@ sphinxext-opengraph==0.9.1 # via plangym sqlalchemy==2.0.32 # via jupyter-cache -stable-retro==0.9.2 +stable-retro==0.9.1 # via plangym stack-data==0.6.3 # via ipython @@ -532,7 +533,7 @@ tornado==6.4.1 tqdm==4.66.5 # via autorom # via dm-control - # via nes-py + # via fragile-nes-py traitlets==5.14.3 # via comm # via ipykernel diff --git a/src/plangym/scripts/import_retro_roms.py b/src/plangym/scripts/import_retro_roms.py index 239fed3..f26a6b7 100644 --- a/src/plangym/scripts/import_retro_roms.py +++ b/src/plangym/scripts/import_retro_roms.py @@ -3,6 +3,7 @@ import zipfile import logging import flogging +from pathlib import Path import retro.data @@ -40,7 +41,7 @@ def main(): ".pce": "PCEngine", } EMU_EXTENSIONS.update(emu_extensions) - paths = sys.argv[1:] or [os.getcwd()] + paths = sys.argv[1:] or [Path.cwd()] logger.info(f"Importing ROMs from: {paths}") logger.info("Fetching known hashes") known_hashes = retro.data.get_known_hashes()