diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..259b7ed --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,42 @@ +image: Visual Studio 2019 + +environment: + matrix: + - { PYTHON: "C:\\Python27", APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" } + - { PYTHON: "C:\\Python35", APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" } + - PYTHON: "C:\\Python36" + - PYTHON: "C:\\Python37" + - PYTHON: "C:\\Python38" + - { PYTHON: "C:\\Python27-x64", APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" } + - { PYTHON: "C:\\Python35-x64", APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015" } + - PYTHON: "C:\\Python36-x64" + - PYTHON: "C:\\Python37-x64" + - PYTHON: "C:\\Python38-x64" + +install: + - "%PYTHON%\\python.exe -m pip install wheel" + +build: off + +before_test: + - "%PYTHON%\\python.exe setup.py bdist_wheel" + - "cd dist" + - "%PYTHON%\\python.exe -m pip install --find-links=. atari-py" + - "%PYTHON%\\python.exe -m pip install gym" + +test_script: + - "%PYTHON%\\python.exe -c \"import gym; env = gym.make('Enduro-v0'); env.reset(); [env.step(env.action_space.sample()) for i in range(1000)]\"" + +artifacts: + - path: dist\*.whl + name: Releases + +deploy: + provider: GitHub + auth_token: + secure: "Weh3LepGE4k174U43zSHYAiM1pUCUSlQ+wjwpmUI2X3SsZqTpdRKzDUwkLnHjp23" + artifact: /.*\.whl/ + draft: false + prerelease: false + on: + appveyor_repo_tag: true diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..cbed0de --- /dev/null +++ b/.travis.yml @@ -0,0 +1,42 @@ +language: sh +script: ./build.sh +os: + - linux + - windows + - osx +env: + - PY_VER=3.6 + - PY_VER=3.7 + - PY_VER=3.5 +osx_image: xcode8.3 +matrix: + exclude: + os: windows + env: PY_VER=3.5 +deploy: + provider: s3 + access_key_id: AKIAIR7X3BP26ZWUNFPA + skip_cleanup: true + secret_access_key: + secure: wUNE0AxIZ3rQVO6pUXQyfOVI4UN/4bgTb+y8566pFYQ68mzZEW3XNl96n8HtkHGYuJGnNGWGbGKcp2a1MCoiyePR+NnmoUxCledUffU+s88Sxg9Qj7DW8JOLImocNXG+8y2OeC+QjmrkB109inM0c2T+ESeLZi9yALaLSxVEKToXB1EBDtOohjelNcA8xoP9yb4H6yoT01XLNkno2o9fmKc+ZXtLi9xZVpMX1aH0Hs2pchm0WmE7d93XVD0K9ZM/2q1yT8ojJngKAbpJeJQSSSJStNpFBIp7hc+2slFP9/QOCPJQZQuIjJkHj2iXEMgIrerbFgl/n5konQiZFHwDZbmubQR8cfVOmr/+By8n9h13rzL7OpqCx84f4QuaRifRY6vMnu0nqbo7M7vkW0fEzcMOEpn1fcL0ulOOFc9Oedyvn08flK2M3DX10rglncK+MkGdWjH23K/1PLNOCIdNO8kWO+yvuCbdx1oJzMcpr5w52Sh+pWt4xXk1TQvXtzPVxLxf+mGumHvU1wSFW2IbBcLIv3C/BeqeNbyRNIrXT2rfaOzDj8LEXOq3IIdfgKmHx2a4Iz3V3S2a5vJiXeBtr9G4LtH/k9eRRyr6CKN+HqSZSRnwL0W8i3u7QvcP6wSF91lJCpAJpsuu5faE3RKfW/hUd/gsmo1f1tcJZEaCkzo= + bucket: games-wheels + local-dir: wheelhouse + upload-dir: atari-py/$TRAVIS_TAG + acl: public_read + on: + repo: openai/atari-py + tags: true + +jobs: + # this extra stage is due to Travis bug that prevets deploying to pypi from windows or usage of + # secret env variables on windows. Instead of doing either, we are deploying to s3 first, and + # then using a separate stage to download files from s3 and deploy them to pypi. + include: + - stage: "Deploy to PyPI" + script: ./deploy_to_pypi.sh + env: + - TWINE_USERNAME=peterz-openai + - AWS_ACCESS_KEY_ID=AKIAIR7X3BP26ZWUNFPA + - AWS_DEFAULT_REGION=us-west-2 + - secure: "i0qbR3wG2OvJWVBKnAVkgtNIPPxoNQz8KBn+c9RBAWZw0XDsjkUwsDZMTXzll1Do24xLb9FVti2iq7GsNvHcCzltSGOLv6UDm2ywJc4Bz/Fyho27A0kYGe6W/FwtEt+VTkx4z0+7tNOb4KVhs26OmIGdKMJwNibnU86rRtzf05Iq7xS+8Mi/9j09oC9ZXACzOFTmbQe98Gq93ROo+7wfpCVJip9QigJTSZhE7axBEtpXlZ5//T2Eo2bxbJs5f9tNiMrLRy8NMYq1B/he9HevpxlqE15+wJs9exaDClWLtsb7trFKSnmrqtUeXI3aUIQwLgQfaYJlvvmSBUtJxxyk5QHLn3+0u6TCA9ZbHaEElXSLrcexjSEp37+HXH0Z69JzTYn0pVX4IvrmuLhgg4X3z7Ys3SyIe8Tq39QubQ8TWM8bzBN5H0VYObY35BmLl68k2AF61Rq0obJS48ekguzf88DwI9UzxYm8LnlsgpyeX1iLJ6Sy9xdP3S/5onP1+bzaDZ7JNMabNmJ22ZjqPyBp+mUyBjfab9ouDS07CNAPmyQV6VWSL8EaD9QawWB0khgKoen+NNFGeivEIgCRanP9Rg7mpw7Px9elwbl2hXGco7EGdZFPV89uHWEhL2nHUmxVjaRUNlSXjDrwI6f8vlMfbHU5PskxlrG047F/is3gQ00=" + - secure: "NZRMo04bBX2X7phT29rQKDklPFVbnqMoUAhrVFjTLxvAqsQdericyalMjXsE79IA02uZEsHfS45P9YXzgHC4uCZI6694kOZfhmBtQ0FxeJ5M3Zx6IRyMhf92rk7F/DRcRBe78JIBkgTXDJCyQoEX1/RIYIz723BSzRGosOJa1xUub8MQ7Rl3TF54j9JAshBNBTpIw1tORWo3K1A8FucQNgI3N7DucWhg+C3D7cxZok5xwz7FoXHt+Xv7wFvCETaNIsmu/3EI15K9Y43XXQ2rBc46g41SDX7f1rwVNl0D8kStjyDdhdQODDdKPMe3dl6Lckz0qMTaoTUnWL91L+qBAFHsC2imWJVNGL+/4iyTRQAsq/hl7GYNcesfm7kCXNcCjSeyx3ZkOOgZ0okFKWZK51RuKjlCcKgDOMSInCW8vR2rRK699wAfePr38t5TO5E2uQ+hF52NMbzDKw9C6azX4UaWuKPio8oxseOWfLKSYZTCVirBikLM8m4LABCZyKiMlTR1SdzxQmrLAxgnWaD+QYY4SoN4fButDjONOARFKF5e3njzVQo2nHmXb1EATOmnQ4pjKcJezSccIcmYeWpPskXWSmJbWi81zwHz/AyqH6fEnAWmVj0RKjrBfyCZCtEGBikQ9KTJuVHupwNT17CyZKUxtxBpYOpFnk+mLLom3Ak=" diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index 0ede910..0000000 --- a/ChangeLog +++ /dev/null @@ -1,56 +0,0 @@ -June 22nd, 2015. ALE 0.5.0. - * Added action_repeat_stochasticity. - * Added sound playback, visualization. - * Added screen/sound recording ability. - * CMake now available. - * Incorporated Benjamin Goodrich's Python interface. - * Some game fixes. - * Added examples for shared library, Python, fifo, RL-Glue interfaces. - * Incorporated Java agent into main repository. - * Removed internal controller, now superseded by shared library interface. - * Better ALEInterface. - * Many other changes. - -February 15th, 2015. ALE 0.5dev. - * Removed the following command-line flags: 'output_file', 'system_reset_steps', 'use_environment_distribution', 'backward_compatible_save', internal agent flags - * The flag 'use_starting_actions' was removed and internally its value is always 'true'. - * The flag 'disable_color_averaging' was renamed to 'color_averaging' and FALSE is its default value. - -April 28th, 2014. ALE 0.4.4. - * Fixed a memory issue in ALEScreen. - -April 26th, 2014. Bug fix (Mayank Daswani). - * Fixed issues with frame numbers not being correctly updated. - * Fixed a bug where total reward was not properly reported under frame skipping. - -January 7th, 2013. ALE 0.4.3. - * Fixed a bug with ALEState's m_frame_number. - -June 12th, 2013. ALE 0.4.2. - * Modified StellaEnvironment save/load interface to provide additional flexibility. - * Series of bug fixes from Matthew Hausknecht and community. - -May 24th, 2013. Bug fix to ALE 0.4.1. - * Fixed RL-Glue syntax from OBSERVATION to OBSERVATIONS. Thanks to Angus MacIsaac for picking this bug up. - -May 22nd, 2013. ALE 0.4.1. - * Added frame skipping support directly in StellaEnvironment. - * Reverted default number of episodes to 10. - -April 22nd, 2013. ALE 0.4.0. - * RL-Glue support - * Shared library interface - * Simpler direct environment interfacing - * Improved environment handling - * Improved environment customization - * Better documentation - -October 3rd, 2012. Moving to GITHub. - -August 7th, 2012. ALE 0.3.1. - * Fixed frames per episode cap for FIFO agents, added functionality to - limit the total number of frames per run for FIFO agents. - -July 22nd, 2012. ALE 0.3. - * Initial ALE release. - diff --git a/Makefile b/Makefile deleted file mode 100644 index 5fd410b..0000000 --- a/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: build clean - -build: - make -C atari_py/ale_interface build - -clean: - rm -rf dist atari_py.egg-info - make -C atari_py/ale_interface clean - -package_data: - ( echo "ale_interface/build/*.so" && echo "ale_interface/build/ale" && cd atari_py && git ls-files |grep -v \\.py$ ) > atari_py/package_data.txt - -upload: - make clean - rm -rf dist - python setup.py sdist - twine upload dist/* diff --git a/README-ale.md b/README-ale.md index 6031d27..edee571 100644 --- a/README-ale.md +++ b/README-ale.md @@ -1,23 +1,119 @@ -(Contents from [https://github.com/bbitmaster/ale_python_interface](https://github.com/bbitmaster/ale_python_interface)) +[![Build Status](https://travis-ci.org/mgbellemare/Arcade-Learning-Environment.svg?branch=master)](https://travis-ci.org/mgbellemare/Arcade-Learning-Environment) -# Arcade-Learning-Environment +# The Arcade Learning Environment -The Arcade Learning Environment (ALE) -- a platform for AI research. +The Arcade Learning Environment (ALE) is a simple object-oriented framework that allows researchers and hobbyists to develop AI agents for Atari 2600 games. It is built on top of the Atari 2600 emulator [Stella](https://stella-emu.github.io/) and separates the details of emulation from agent design. This [video](https://www.youtube.com/watch?v=nzUiEkasXZI) depicts over 50 games currently supported in the ALE. -For more details and installation instructions, see the [website](http://www.arcadelearningenvironment.org) and [manual](doc/manual/manual.pdf). To ask questions and discuss, please join the [ALE-users group](https://groups.google.com/forum/#!forum/arcade-learning-environment). +For an overview of our goals for the ALE read [The Arcade Learning Environment: An Evaluation Platform for General Agents](http://www.jair.org/papers/paper3912.html). If you use ALE in your research, we ask that you please cite this paper in reference to the environment (BibTeX entry at the end of this document). Also, if you have any questions or comments about the ALE, please contact us through our [mailing list](https://groups.google.com/forum/#!forum/arcade-learning-environment). -## Citing ALE -If ALE helps in your research, please cite the following: +Feedback and suggestions are welcome and may be addressed to any active member of the ALE team. - @article{bellemare13arcade, - author = {{Bellemare}, M.~G. and {Naddaf}, Y. and {Veness}, J. and {Bowling}, M.}, - title = {The Arcade Learning Environment: An Evaluation Platform for General Agents}, - journal = {Journal of Artificial Intelligence Research}, - year = "2013", - month = "jun", - volume = "47", - pages = "253--279", - } +### Features +- Object-oriented framework with support to add agents and games. +- Emulation core uncoupled from rendering and sound generation modules for fast emulation with minimal library dependencies. +- Automatic extraction of game score and end-of-game signal for more than 50 Atari 2600 games. +- Multi-platform code (compiled and tested under OS X and several Linux distributions, with Cygwin support). +- Communication between agents and emulation core can be accomplished through pipes, allowing for cross-language development (sample Java code included). +- Python development is supported through ctypes. +- Agents programmed in C++ have access to all features in the ALE. +- Visualization tools. + +## Quick start + + +Install main dependences: +``` +sudo apt-get install libsdl1.2-dev libsdl-gfx1.2-dev libsdl-image1.2-dev cmake +``` + +Compilation: + +``` +$ mkdir build && cd build +$ cmake -DUSE_SDL=ON -DUSE_RLGLUE=OFF -DBUILD_EXAMPLES=ON .. +$ make -j 4 +``` + +To install python module: + +``` +$ pip install . +or +$ pip install --user . +``` + +Getting the ALE to work on Visual Studio requires a bit of extra wrangling. You may wish to use IslandMan93's [Visual Studio port of the ALE.](https://github.com/Islandman93/Arcade-Learning-Environment) + +For more details and installation instructions, see the [manual](doc/manual/manual.pdf). To ask questions and discuss, please join the [ALE-users group](https://groups.google.com/forum/#!forum/arcade-learning-environment). + +## ALE releases + +Releases before v.0.5 are available for download in our previous [website](http://www.arcadelearningenvironment.org/). For the latest releases, please check our releases [page](https://github.com/mgbellemare/Arcade-Learning-Environment/releases). + +## List of command-line parameters + +Execute ./ale -help for more details; alternatively, see documentation +available at http://www.arcadelearningenvironment.org. + +``` +-random_seed [n] -- sets the random seed; defaults to the current time + +-game_controller [fifo|fifo_named] -- specifies how agents interact + with the ALE; see Java agent documentation for details + +-config [file] -- specifies a configuration file, from which additional + parameters are read + +-run_length_encoding [false|true] -- determine whether run-length encoding is + used to send data over pipes; irrelevant when an internal agent is + being used + +-max_num_frames_per_episode [n] -- sets the maximum number of frames per + episode. Once this number is reached, a new episode will start. Currently + implemented for all agents when using pipes (fifo/fifo_named) + +-max_num_frames [n] -- sets the maximum number of frames (independent of how + many episodes are played) +``` + + +## Citing The Arcade Learning Environment + + +If you use the ALE in your research, we ask that you please cite the following. + +*M. G. Bellemare, Y. Naddaf, J. Veness and M. Bowling. The Arcade Learning Environment: An Evaluation Platform for General Agents, Journal of Artificial Intelligence Research, Volume 47, pages 253-279, 2013.* + +In BibTeX format: + +``` +@Article{bellemare13arcade, + author = {{Bellemare}, M.~G. and {Naddaf}, Y. and {Veness}, J. and {Bowling}, M.}, + title = {The Arcade Learning Environment: An Evaluation Platform for General Agents}, + journal = {Journal of Artificial Intelligence Research}, + year = "2013", + month = "jun", + volume = "47", + pages = "253--279", +} +``` + + +If you use the ALE with sticky actions (flag `repeat_action_probability`), or if you use the different game flavours (mode and difficulty switches), we ask you that you also cite the following: + +*M. C. Machado, M. G. Bellemare, E. Talvitie, J. Veness, M. J. Hausknecht, M. Bowling. Revisiting the Arcade Learning Environment: Evaluation Protocols and Open Problems for General Agents, CoRR abs/1709.06009, 2017.* + +In BibTex format: + +``` +@Article{machado17arcade, + author = {Marlos C. Machado and Marc G. Bellemare and Erik Talvitie and Joel Veness and Matthew J. Hausknecht and Michael Bowling}, + title = {Revisiting the Arcade Learning Environment: Evaluation Protocols and Open Problems for General Agents}, + journal = {CoRR}, + volume = {abs/1709.06009}, + year = {2017} +} +``` diff --git a/README.md b/README.md index 76ae5f4..6a7c463 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,13 @@ # atari_py -A packaged and slightly-modified version of [https://github.com/bbitmaster/ale_python_interface](https://github.com/bbitmaster/ale_python_interface). +An `openai/atari-py` fork with Windows support and removed zlib/libpng dependencies. ## Installation -To install via pip, run: +To simply install [`atari-py` wheels (binaries)](https://github.com/Kojoley/atari-py/releases) use this command: -```pip install atari-py``` +```pip install -f https://github.com/Kojoley/atari-py/releases atari_py``` -Alternatively, you can install using setuptools using +If you have any `distutils` supported compiler you can install from sources: -```python setup.py install``` - -You can also trigger a build of the C++ code via `make`, and then add -this repo to your `PYTHONPATH`: - -```export PYTHONPATH=/path/to/atari-py:$PYTHONPATH``` - -### Common issues - -- Make sure you have `cmake` installed. On OSX, you probably want - `brew install cmake`. +```pip install git+https://github.com/Kojoley/atari-py.git``` diff --git a/atari_py/__init__.py b/atari_py/__init__.py index 6dd7b48..e09a7bd 100644 --- a/atari_py/__init__.py +++ b/atari_py/__init__.py @@ -1,4 +1,4 @@ -from ale_python_interface import * +from .ale_python_interface import * import os def _game_dir(): @@ -9,4 +9,7 @@ def get_game_path(game_name): def list_games(): files = os.listdir(_game_dir()) - return [os.path.basename(f).split(".")[0] for f in files] \ No newline at end of file + return [os.path.basename(f).split(".")[0] for f in files] + +# default to only logging errors +ALEInterface.setLoggerMode(ALEInterface.Logger.Error) diff --git a/atari_py/ale_c_wrapper.cpp b/atari_py/ale_c_wrapper.cpp index 2ddd746..30f6073 100644 --- a/atari_py/ale_c_wrapper.cpp +++ b/atari_py/ale_c_wrapper.cpp @@ -1,3 +1,25 @@ -//all code is currently in the .h file #include "ale_c_wrapper.h" +#include +#include +#include + +void encodeState(ALEState *state, char *buf, int buf_len) { + std::string str = state->serialize(); + + if (buf_len < int(str.length())) { + throw new std::runtime_error("Buffer is not big enough to hold serialized ALEState. Please use encodeStateLen to determine the correct buffer size"); + } + + memcpy(buf, str.data(), str.length()); +} + +int encodeStateLen(ALEState *state) { + return state->serialize().length(); +} + +ALEState *decodeState(const char *serialized, int len) { + std::string str(serialized, len); + + return new ALEState(str); +} \ No newline at end of file diff --git a/atari_py/ale_c_wrapper.h b/atari_py/ale_c_wrapper.h index 1666591..21eac34 100644 --- a/atari_py/ale_c_wrapper.h +++ b/atari_py/ale_c_wrapper.h @@ -3,104 +3,131 @@ #include +#ifdef _WIN32 +# define SYMBOL_EXPORT __declspec(dllexport) +#endif +#ifndef SYMBOL_EXPORT +# define SYMBOL_EXPORT +#endif + extern "C" { // Declares int rgb_palette[256] #include "atari_ntsc_rgb_palette.h" - ALEInterface *ALE_new() {return new ALEInterface();} - void ALE_del(ALEInterface *ale){delete ale;} - const char *getString(ALEInterface *ale, const char *key){return ale->getString(key).c_str();} - int getInt(ALEInterface *ale,const char *key) {return ale->getInt(key);} - bool getBool(ALEInterface *ale,const char *key){return ale->getBool(key);} - float getFloat(ALEInterface *ale,const char *key){return ale->getFloat(key);} - void setString(ALEInterface *ale,const char *key,const char *value){ale->setString(key,value);} - void setInt(ALEInterface *ale,const char *key,int value){ale->setInt(key,value);} - void setBool(ALEInterface *ale,const char *key,bool value){ale->setBool(key,value);} - void setFloat(ALEInterface *ale,const char *key,float value){ale->setFloat(key,value);} - void loadROM(ALEInterface *ale,const char *rom_file){ale->loadROM(rom_file);} - int act(ALEInterface *ale,int action){return ale->act((Action)action);} - bool game_over(ALEInterface *ale){return ale->game_over();} - void reset_game(ALEInterface *ale){ale->reset_game();} - void getLegalActionSet(ALEInterface *ale,int *actions){ + SYMBOL_EXPORT ALEInterface *ALE_new() {return new ALEInterface();} + SYMBOL_EXPORT void ALE_del(ALEInterface *ale){delete ale;} + SYMBOL_EXPORT const char *getString(ALEInterface *ale, const char *key){return ale->getString(key).c_str();} + SYMBOL_EXPORT int getInt(ALEInterface *ale,const char *key) {return ale->getInt(key);} + SYMBOL_EXPORT bool getBool(ALEInterface *ale,const char *key){return ale->getBool(key);} + SYMBOL_EXPORT float getFloat(ALEInterface *ale,const char *key){return ale->getFloat(key);} + SYMBOL_EXPORT void setString(ALEInterface *ale,const char *key,const char *value){ale->setString(key,value);} + SYMBOL_EXPORT void setInt(ALEInterface *ale,const char *key,int value){ale->setInt(key,value);} + SYMBOL_EXPORT void setBool(ALEInterface *ale,const char *key,bool value){ale->setBool(key,value);} + SYMBOL_EXPORT void setFloat(ALEInterface *ale,const char *key,float value){ale->setFloat(key,value);} + SYMBOL_EXPORT bool loadROM(ALEInterface *ale,const char *rom,size_t size,const char *name){return ale->loadROM(std::string(rom, size), name);} + SYMBOL_EXPORT int act(ALEInterface *ale,int action){return ale->act((Action)action);} + SYMBOL_EXPORT bool game_over(ALEInterface *ale){return ale->game_over();} + SYMBOL_EXPORT void reset_game(ALEInterface *ale){ale->reset_game();} + SYMBOL_EXPORT void getAvailableModes(ALEInterface *ale,int *availableModes) { + ModeVect modes_vect = ale->getAvailableModes(); + for(unsigned int i = 0; i < ale->getAvailableModes().size(); i++){ + availableModes[i] = modes_vect[i]; + } + } + SYMBOL_EXPORT int getAvailableModesSize(ALEInterface *ale) {return ale->getAvailableModes().size();} + SYMBOL_EXPORT void setMode(ALEInterface *ale, int mode) {ale->setMode(mode);} + SYMBOL_EXPORT void getAvailableDifficulties(ALEInterface *ale,int *availableDifficulties) { + DifficultyVect difficulties_vect = ale->getAvailableDifficulties(); + for(unsigned int i = 0; i < ale->getAvailableDifficulties().size(); i++){ + availableDifficulties[i] = difficulties_vect[i]; + } + } + SYMBOL_EXPORT int getAvailableDifficultiesSize(ALEInterface *ale) {return ale->getAvailableDifficulties().size();} + SYMBOL_EXPORT void setDifficulty(ALEInterface *ale, int difficulty) {ale->setDifficulty(difficulty);} + SYMBOL_EXPORT void getLegalActionSet(ALEInterface *ale,int *actions){ ActionVect action_vect = ale->getLegalActionSet(); - for(unsigned int i = 0;i < ale->getLegalActionSet().size();i++){ + for(unsigned int i = 0; i < ale->getLegalActionSet().size(); i++){ actions[i] = action_vect[i]; } } - int getLegalActionSize(ALEInterface *ale){return ale->getLegalActionSet().size();} - void getMinimalActionSet(ALEInterface *ale,int *actions){ + SYMBOL_EXPORT int getLegalActionSize(ALEInterface *ale){return ale->getLegalActionSet().size();} + SYMBOL_EXPORT void getMinimalActionSet(ALEInterface *ale,int *actions){ ActionVect action_vect = ale->getMinimalActionSet(); for(unsigned int i = 0;i < ale->getMinimalActionSet().size();i++){ actions[i] = action_vect[i]; } } - int getMinimalActionSize(ALEInterface *ale){return ale->getMinimalActionSet().size();} - int getFrameNumber(ALEInterface *ale){return ale->getFrameNumber();} - int lives(ALEInterface *ale){return ale->lives();} - int getEpisodeFrameNumber(ALEInterface *ale){return ale->getEpisodeFrameNumber();} - void getScreen(ALEInterface *ale,unsigned char *screen_data){ + SYMBOL_EXPORT int getMinimalActionSize(ALEInterface *ale){return ale->getMinimalActionSet().size();} + SYMBOL_EXPORT int getFrameNumber(ALEInterface *ale){return ale->getFrameNumber();} + SYMBOL_EXPORT int lives(ALEInterface *ale){return ale->lives();} + SYMBOL_EXPORT int getEpisodeFrameNumber(ALEInterface *ale){return ale->getEpisodeFrameNumber();} + SYMBOL_EXPORT void getScreen(ALEInterface *ale,unsigned char *screen_data){ int w = ale->getScreen().width(); int h = ale->getScreen().height(); pixel_t *ale_screen_data = (pixel_t *)ale->getScreen().getArray(); memcpy(screen_data,ale_screen_data,w*h*sizeof(pixel_t)); } - void getRAM(ALEInterface *ale,unsigned char *ram){ + SYMBOL_EXPORT void getRAM(ALEInterface *ale,unsigned char *ram){ unsigned char *ale_ram = ale->getRAM().array(); int size = ale->getRAM().size(); memcpy(ram,ale_ram,size*sizeof(unsigned char)); } - int getRAMSize(ALEInterface *ale){return ale->getRAM().size();} - int getScreenWidth(ALEInterface *ale){return ale->getScreen().width();} - int getScreenHeight(ALEInterface *ale){return ale->getScreen().height();} + SYMBOL_EXPORT int getRAMSize(ALEInterface *ale){return ale->getRAM().size();} + SYMBOL_EXPORT int getScreenWidth(ALEInterface *ale){return ale->getScreen().width();} + SYMBOL_EXPORT int getScreenHeight(ALEInterface *ale){return ale->getScreen().height();} - void getScreenRGB(ALEInterface *ale, int *output_buffer){ + SYMBOL_EXPORT void getScreenRGB(ALEInterface *ale, unsigned char *output_buffer){ size_t w = ale->getScreen().width(); size_t h = ale->getScreen().height(); size_t screen_size = w*h; pixel_t *ale_screen_data = ale->getScreen().getArray(); - for(int i = 0;i < w*h;i++){ - output_buffer[i] = rgb_palette[ale_screen_data[i]]; - } - + ale->theOSystem->colourPalette().applyPaletteRGB(output_buffer, ale_screen_data, screen_size ); } - void getScreenGrayscale(ALEInterface *ale, unsigned char *output_buffer){ + + SYMBOL_EXPORT void getScreenRGB2(ALEInterface *ale, unsigned char *output_buffer){ size_t w = ale->getScreen().width(); size_t h = ale->getScreen().height(); size_t screen_size = w*h; pixel_t *ale_screen_data = ale->getScreen().getArray(); - ale->theOSystem->colourPalette().applyPaletteGrayscale(output_buffer, ale_screen_data, screen_size); - } - - void saveState(ALEInterface *ale){ale->saveState();} - void loadState(ALEInterface *ale){ale->loadState();} - void saveScreenPNG(ALEInterface *ale,const char *filename){ale->saveScreenPNG(filename);} - - ALEState* cloneState(ALEInterface *ale) { - return new ALEState(ale->cloneState()); - } - - void ALEState_del(ALEState* state) { - delete state; - } - - void restoreState(ALEInterface *ale, ALEState* state) { - ale->restoreState(*state); + int j = 0; + for(int i = 0;i < screen_size;i++){ + unsigned int zrgb = rgb_palette[ale_screen_data[i]]; + output_buffer[j++] = (zrgb>>16)&0xff; + output_buffer[j++] = (zrgb>>8)&0xff; + output_buffer[j++] = (zrgb>>0)&0xff; + } } - int ALEState_getFrameNumber(ALEState* state) { - return state->getFrameNumber(); - } - int ALEState_getEpisodeFrameNumber(ALEState* state) { - return state->getEpisodeFrameNumber(); - } + SYMBOL_EXPORT void getScreenGrayscale(ALEInterface *ale, unsigned char *output_buffer){ + size_t w = ale->getScreen().width(); + size_t h = ale->getScreen().height(); + size_t screen_size = w*h; + pixel_t *ale_screen_data = ale->getScreen().getArray(); - bool ALEState_equals(ALEState* a, ALEState *b) { - return a->equals(*b); + ale->theOSystem->colourPalette().applyPaletteGrayscale(output_buffer, ale_screen_data, screen_size); } + SYMBOL_EXPORT void saveState(ALEInterface *ale){ale->saveState();} + SYMBOL_EXPORT void loadState(ALEInterface *ale){ale->loadState();} + SYMBOL_EXPORT ALEState* cloneState(ALEInterface *ale){return new ALEState(ale->cloneState());} + SYMBOL_EXPORT void restoreState(ALEInterface *ale, ALEState* state){ale->restoreState(*state);} + SYMBOL_EXPORT ALEState* cloneSystemState(ALEInterface *ale){return new ALEState(ale->cloneSystemState());} + SYMBOL_EXPORT void restoreSystemState(ALEInterface *ale, ALEState* state){ale->restoreSystemState(*state);} + SYMBOL_EXPORT void deleteState(ALEState* state){delete state;} + //Kojoley SYMBOL_EXPORT void saveScreenPNG(ALEInterface *ale,const char *filename){ale->saveScreenPNG(filename);} + + // Encodes the state as a raw bytestream. This may have multiple '\0' characters + // and thus should not be treated as a C string. Use encodeStateLen to find the length + // of the buffer to pass in, or it will be overrun as this simply memcpys bytes into the buffer. + SYMBOL_EXPORT void encodeState(ALEState *state, char *buf, int buf_len); + SYMBOL_EXPORT int encodeStateLen(ALEState *state); + SYMBOL_EXPORT ALEState *decodeState(const char *serialized, int len); + + // 0: Info, 1: Warning, 2: Error + SYMBOL_EXPORT void setLoggerMode(int mode) { ale::Logger::setMode(ale::Logger::mode(mode)); } } #endif diff --git a/atari_py/ale_interface/CMakeLists.txt b/atari_py/ale_interface/CMakeLists.txt index 140ef1b..ec9c6af 100644 --- a/atari_py/ale_interface/CMakeLists.txt +++ b/atari_py/ale_interface/CMakeLists.txt @@ -1,13 +1,20 @@ cmake_minimum_required (VERSION 2.6) project(ale) +set(ALEVERSION "0.6") + option(USE_SDL "Use SDL" OFF) option(USE_RLGLUE "Use RL-Glue" OFF) option(BUILD_EXAMPLES "Build Example Agents" OFF) +option(BUILD_CPP_LIB "Build C++ Shared Library" OFF) +option(BUILD_CLI "Build ALE Command Line Interface" OFF) +option(BUILD_C_LIB "Build ALE C Library (needed for Python interface)" ON) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wunused -fPIC -O3 -fomit-frame-pointer -D__STDC_CONSTANT_MACROS") -add_definitions(-DHAVE_INTTYPES) -set(LINK_LIBS z) +find_package(ZLIB REQUIRED) +if(ZLIB_FOUND) + include_directories(${ZLIB_INCLUDE_DIRS}) + set(LINK_LIBS ${ZLIB_LIBRARIES}) +endif() if(USE_RLGLUE) add_definitions(-D__USE_RLGLUE) @@ -23,8 +30,9 @@ if(USE_SDL) list(APPEND LINK_LIBS ${SDL_LIBRARY} ${SDL_MAIN_LIBRARY}) else() MESSAGE("SDL 1.2 not found: You may need to manually edit CMakeLists.txt or run \"cmake -i\" to specify your SDL path.") - # Uncomment below to specify the path to your SDL library. Run "locate libSDL" if unsure. + # Uncomment below to specify the path to your SDL library and header file. Run "locate libSDL" and "locate SDL.h" if unsure. # link_directories(path_to_your_SDL) + # include_directories(path_to_your_SDL_header) if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -framework Cocoa") list(APPEND LINK_LIBS sdl sdlmain) @@ -46,18 +54,71 @@ endforeach(module ${MODULES}) # OS-dependent specifics if(APPLE) + add_definitions(-DBSPF_MAC_OSX) include_directories(/System/Library/Frameworks/vecLib.framework/Versions/Current/Headers) set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") endif() -if(WINDOWS OR MINGW) +if(WIN32 OR MINGW) + add_definitions(-DBSPF_WIN32) list(APPEND SOURCES ${SOURCE_DIR}/os_dependent/SettingsWin32.cxx ${SOURCE_DIR}/os_dependent/OSystemWin32.cxx ${SOURCE_DIR}/os_dependent/FSNodeWin32.cxx) else() + add_definitions(-DBSPF_UNIX) list(APPEND SOURCES ${SOURCE_DIR}/os_dependent/SettingsUNIX.cxx ${SOURCE_DIR}/os_dependent/OSystemUNIX.cxx ${SOURCE_DIR}/os_dependent/FSNodePOSIX.cxx) + SET(BIN_INSTALL_DIR "bin") + SET(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) + SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE STRING "Library directory name") + SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE STRING "Headers directory name") + SET(PKGCONFIG_INSTALL_DIR "${LIB_INSTALL_DIR}/pkgconfig/" CACHE STRING "Base directory for pkgconfig files") + endif() +# List and set the install targets for the headers, generate and install the pkgconfig file +if(UNIX) + + INSTALL(FILES ${SOURCE_DIR}/os_dependent/SettingsUNIX.hxx ${SOURCE_DIR}/os_dependent/SettingsWin32.hxx ${SOURCE_DIR}/os_dependent/OSystemUNIX.hxx ${SOURCE_DIR}/os_dependent/OSystemWin32.hxx DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/os_dependent) + file(GLOB module_headers ${SOURCE_DIR}/*.h?[xp]) + foreach(header ${module_headers}) + INSTALL(FILES ${header} DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}) + endforeach(header ${HEADERS}) + + foreach(module ${MODULES}) + file(GLOB module_headers ${SOURCE_DIR}/${module}/*.h) + foreach(header ${module_headers}) + INSTALL(FILES ${header} DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/${module}/) + endforeach(header ${HEADERS}) + + file(GLOB module_headers ${SOURCE_DIR}/${module}/*.h?[xp]) + foreach(header ${module_headers}) + INSTALL(FILES ${header} DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/${module}/) + endforeach(header ${HEADERS}) + endforeach(module ${MODULES}) + + ################################### + # Pkg-config stuff + ################################### + + FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc +" +Name: ${PROJECT_NAME} +Description: The Arcade Learning Environment (ALE) - a platform for AI research. +URL: http://www.arcadelearningenvironment.org/ +Version: ${ALEVERSION} +Requires: +Libs: -L${LIB_INSTALL_DIR} -lale +Cflags: -I${INCLUDE_INSTALL_DIR} +" +) + + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc + DESTINATION ${PKGCONFIG_INSTALL_DIR}) + +endif(UNIX) + include_directories( ${SOURCE_DIR} + ${SOURCE_DIR}/zlib ${SOURCE_DIR}/common ${SOURCE_DIR}/controllers ${SOURCE_DIR}/emucore @@ -71,31 +132,77 @@ include_directories( ${SOURCE_DIR}/external/TinyMT ) -add_library(ale-lib SHARED ${SOURCE_DIR}/ale_interface.cpp ${SOURCES}) -set_target_properties(ale-lib PROPERTIES OUTPUT_NAME ale) -set_target_properties(ale-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -add_executable(ale-bin ${SOURCE_DIR}/main.cpp ${SOURCE_DIR}/ale_interface.cpp ${SOURCES}) -set_target_properties(ale-bin PROPERTIES OUTPUT_NAME ale) -set_target_properties(ale-bin PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -add_library(ale-c-lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/../ale_c_wrapper.cpp ${SOURCE_DIR}/ale_interface.cpp ${SOURCES}) -set_target_properties(ale-c-lib PROPERTIES OUTPUT_NAME ale_c) -set_target_properties(ale-c-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +if(NOT BUILD_CPP_LIB AND BUILD_EXAMPLES) + set(BUILD_CPP_LIB ON) + MESSAGE("Enabling C++ library to support examples.") +endif() + +if(BUILD_CPP_LIB) + add_library(ale-lib SHARED ${SOURCE_DIR}/ale_interface.cpp ${SOURCES}) + set_target_properties(ale-lib PROPERTIES OUTPUT_NAME ale) + set_target_properties(ale-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + if(UNIX) + install(TARGETS ale-lib + DESTINATION ${LIB_INSTALL_DIR}) + endif() + target_link_libraries(ale-lib ${LINK_LIBS}) +endif() + +if(BUILD_CLI) + add_executable(ale-bin ${SOURCE_DIR}/main.cpp ${SOURCE_DIR}/ale_interface.cpp ${SOURCES}) + set_target_properties(ale-bin PROPERTIES OUTPUT_NAME ale) + set_target_properties(ale-bin PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + if(UNIX) + install(TARGETS ale-bin + DESTINATION ${BIN_INSTALL_DIR}) + endif() + target_link_libraries(ale-bin ${LINK_LIBS}) +endif() -target_link_libraries(ale-lib ${LINK_LIBS}) -target_link_libraries(ale-bin ${LINK_LIBS}) -target_link_libraries(ale-c-lib ${LINK_LIBS}) +if(BUILD_C_LIB) + add_library(ale-c-lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/../ale_c_wrapper.cpp ${SOURCE_DIR}/ale_interface.cpp ${SOURCES}) + set_target_properties(ale-c-lib PROPERTIES OUTPUT_NAME ale_c) + set_target_properties(ale-c-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + if(UNIX) + install(TARGETS ale-c-lib + DESTINATION ${LIB_INSTALL_DIR}) + endif() + target_link_libraries(ale-c-lib ${LINK_LIBS}) +endif() if(BUILD_EXAMPLES) + # Shared library example. link_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_executable(sharedLibraryInterfaceExample ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples/sharedLibraryInterfaceExample.cpp) set_target_properties(sharedLibraryInterfaceExample PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples) + set_target_properties(sharedLibraryInterfaceExample PROPERTIES OUTPUT_NAME ${PROJECT_NAME}-sharedLibraryInterfaceExample) + target_link_libraries(sharedLibraryInterfaceExample ale) target_link_libraries(sharedLibraryInterfaceExample ${LINK_LIBS}) add_dependencies(sharedLibraryInterfaceExample ale-lib) - + + # Fifo interface example. + link_directories(${CMAKE_CURRENT_SOURCE_DIR}) + + add_executable(fifoInterfaceExample ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples/fifoInterfaceExample.cpp) + set_target_properties(fifoInterfaceExample PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples) + set_target_properties(fifoInterfaceExample PROPERTIES OUTPUT_NAME ${PROJECT_NAME}-fifoInterfaceExample) + target_link_libraries(fifoInterfaceExample ale) + target_link_libraries(fifoInterfaceExample ${LINK_LIBS}) + add_dependencies(fifoInterfaceExample ale-lib) + + add_executable(sharedLibraryInterfaceWithModesExample ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples/sharedLibraryInterfaceWithModesExample.cpp) + set_target_properties(sharedLibraryInterfaceWithModesExample PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples) + set_target_properties(sharedLibraryInterfaceWithModesExample PROPERTIES OUTPUT_NAME ${PROJECT_NAME}-sharedLibraryInterfaceWithModesExample) + target_link_libraries(sharedLibraryInterfaceWithModesExample ale) + target_link_libraries(sharedLibraryInterfaceWithModesExample ${LINK_LIBS}) + add_dependencies(sharedLibraryInterfaceWithModesExample ale-lib) + + # Example showing how to record an Atari 2600 video. if (USE_SDL) add_executable(videoRecordingExample ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples/videoRecordingExample.cpp) set_target_properties(videoRecordingExample PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples) + set_target_properties(videoRecordingExample PROPERTIES OUTPUT_NAME ${PROJECT_NAME}-videoRecordingExample) target_link_libraries(videoRecordingExample ale) target_link_libraries(videoRecordingExample ${LINK_LIBS}) add_dependencies(videoRecordingExample ale-lib) @@ -105,12 +212,49 @@ endif() if(USE_RLGLUE) add_executable(RLGlueAgent ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples/RLGlueAgent.c) set_target_properties(RLGlueAgent PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples) + set_target_properties(RLGlueAgent PROPERTIES OUTPUT_NAME ${PROJECT_NAME}-RLGlueAgent) target_link_libraries(RLGlueAgent rlutils) target_link_libraries(RLGlueAgent rlagent) target_link_libraries(RLGlueAgent rlgluenetdev) + add_executable(RLGlueExperiment ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples/RLGlueExperiment.c) set_target_properties(RLGlueExperiment PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/examples) + set_target_properties(RLGlueExperiment PROPERTIES OUTPUT_NAME ${PROJECT_NAME}-RLGlueExperiment) target_link_libraries(RLGlueExperiment rlutils) target_link_libraries(RLGlueExperiment rlexperiment) target_link_libraries(RLGlueExperiment rlgluenetdev) + endif() + + +########### Add uninstall target ############### + FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake +" +IF(NOT EXISTS \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\") + MESSAGE(FATAL_ERROR \"Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\") +ENDIF(NOT EXISTS \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\") + +FILE(READ \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\" files) +STRING(REGEX REPLACE \"\\n\" \";\" files \"\${files}\") +FOREACH(file \${files}) + MESSAGE(STATUS \"Uninstalling \"\$ENV{DESTDIR}\${file}\"\") + IF(EXISTS \"\$ENV{DESTDIR}\${file}\") + EXEC_PROGRAM( + \"@CMAKE_COMMAND@\" ARGS \"-E remove \"\$ENV{DESTDIR}\${file}\"\" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT \"\${rm_retval}\" STREQUAL 0) + MESSAGE(FATAL_ERROR \"Problem when removing \"\$ENV{DESTDIR}\${file}\"\") + ENDIF(NOT \"\${rm_retval}\" STREQUAL 0) + ELSE(EXISTS \"\$ENV{DESTDIR}\${file}\") + MESSAGE(STATUS \"File \"\$ENV{DESTDIR}\${file}\" does not exist.\") + ENDIF(EXISTS \"\$ENV{DESTDIR}\${file}\") +ENDFOREACH(file) +") + +ADD_CUSTOM_TARGET(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + COMMAND rm -rf ${INCLUDE_INSTALL_DIR}/ale) + + diff --git a/atari_py/ale_interface/Makefile b/atari_py/ale_interface/Makefile deleted file mode 100644 index e77c530..0000000 --- a/atari_py/ale_interface/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -.PHONY: build clean - -build: - mkdir -p build && cd build && cmake .. && make -j4 - -clean: - rm -rf build diff --git a/atari_py/ale_interface/common.rules b/atari_py/ale_interface/common.rules deleted file mode 100644 index 285cd64..0000000 --- a/atari_py/ale_interface/common.rules +++ /dev/null @@ -1,31 +0,0 @@ -# Common build rules, used by the sub modules and their module.mk files - -# Copy the list of objects to a new variable. The name of the new variable -# contains the module name, a trick we use so we can keep multiple different -# module object lists, one for each module. -MODULE_OBJS-$(MODULE) := $(MODULE_OBJS) - -MODULE_LIB-$(MODULE) := $(MODULE)/lib$(notdir $(MODULE)).a - -# If not building as a plugin, add the object files to the main OBJS list -#OBJS += $(MODULE_LIB-$(MODULE)) -OBJS += $(MODULE_OBJS) - -# Convenience library target -#$(MODULE_LIB-$(MODULE)): $(MODULE_OBJS) -# -$(RM) $@ -# $(AR) $@ $+ -# $(RANLIB) $@ - -# Pseudo target for comfort, allows for "make common", "make gui" etc. -#$(MODULE): $(MODULE_LIB-$(MODULE)) - - -# Clean target, removes all object files. This looks a bit hackish, as we have to -# copy the content of MODULE_OBJS to another unique variable (the next module.mk -# will overwrite it after all). The same for the libMODULE.a library file. -clean: clean-$(MODULE) -clean-$(MODULE): clean-% : - -$(RM) $(MODULE_OBJS-$*) $(MODULE_LIB-$*) $(PLUGIN-$*) - -.PHONY: clean-$(MODULE) $(MODULE) diff --git a/atari_py/ale_interface/makefile.mac b/atari_py/ale_interface/makefile.mac deleted file mode 100644 index 3f2f179..0000000 --- a/atari_py/ale_interface/makefile.mac +++ /dev/null @@ -1,248 +0,0 @@ -##============================================================================ -# makefile for the A.L.E: Atari Learing Environment (Powered by Stella) -##============================================================================ - -srcdir ?= . -# Set this to 1 to enable SDL and display_screen -USE_SDL := 0 -# Set this to 1 to enable the RLGlue interface -USE_RLGLUE := 0 -DEFINES := -DRLGENV_NOMAINLOOP -LDFLAGS := -# /opt/local/include for MacPorts; /usr/local/include for RL-Glue -INCLUDES := -Isrc/controllers -Isrc/os_dependent -I/usr/include -I/opt/local/include -I/usr/local/include -Isrc/environment -Isrc/external -LIBS_SDL := -lSDL -lSDLmain -LIBS_RLGLUE := -lrlutils -lrlgluenetdev -LIBS := -L/opt/local/lib -OBJS := -PROF := -MODULES := -MODULE_DIRS := -DISTNAME := atari_learning_environment -DEBUG := 0 - -# -------- Generated by configure ----------- -CXX := g++ -CXXFLAGS := -LD := g++ -# LIBS += -lz -lX11 -# For the Mac -LIBS += -lz -framework Cocoa -RANLIB := ranlib -INSTALL := install -AR := ar cru -MKDIR := mkdir -p -ECHO := printf -CAT := cat -RM := rm -f -RM_REC := rm -f -r -ZIP := zip -q -CP := cp - - -# MODULES += src/unix -MODULE_DIRS += -EXEEXT := -NASM := /usr/bin/nasm -NASMFLAGS := -f elf - -PREFIX := /usr/local -BINDIR := /usr/local/bin -DOCDIR := /usr/local/share/doc/stella -DATADIR := /usr/local/share -PROFILE := - -HAVE_GCC3 = 1 -HAVE_NASM = 1 - -INCLUDES += -Isrc/games -Isrc/emucore -Isrc/emucore/m6502/src -Isrc/emucore/m6502/src/bspf/src -Isrc/common -Isrc/controllers -Isrc/environment -OBJS += -DEFINES += -DUNIX -DHAS_ALTIVEC -DUSE_NASM -DBSPF_UNIX -DHAVE_INTTYPES -DWINDOWED_SUPPORT -DHAVE_GETTIMEOFDAY -DSNAPSHOT_SUPPORT -LDFLAGS += - -ifeq ("$(USE_SDL)", "1") - CXXFLAGS+=$(shell sdl-config --cflags) - LDFLAGS+=$(shell sdl-config --libs) - DEFINES += -D__USE_SDL -DSOUND_SUPPORT -endif - -ifeq ("$(USE_RLGLUE)", "1") - DEFINES += -D__USE_RLGLUE - LIBS += $(LIBS_RLGLUE) -endif - -# Uncomment this for stricter compile time code verification -# CXXFLAGS+= -Werror - -CXXFLAGS += -Wall -Wunused -fPIC - -ifeq ("$(DEBUG)", "1") - CXXFLAGS += -g -O0 -else - CXXFLAGS += -O3 -endif - -ifdef PROFILE - PROF:= -g -pg -fprofile-arcs -ftest-coverage - CXXFLAGS+= $(PROF) -else - CXXFLAGS+= -fomit-frame-pointer -endif - -# Even more warnings... -#CXXFLAGS+= -pedantic -Wpointer-arith -Wcast-qual -Wconversion -#CXXFLAGS+= -Wshadow -Wimplicit -Wundef -Wnon-virtual-dtor -#CXXFLAGS+= -Wno-reorder -Wwrite-strings -fcheck-new -Wctor-dtor-privacy - -####################################################################### -# Misc stuff - you should never have to edit this # -####################################################################### - -EXECUTABLE := ale$(EXEEXT) -LIBRARY := libale.so - -all: tags $(EXECUTABLE) $(LIBRARY) - - -###################################################################### -# Various minor settings -###################################################################### - -# The name for the directory used for dependency tracking -DEPDIR := .deps - - -###################################################################### -# Module settings -###################################################################### - -MODULES := $(MODULES) - -# After the game specific modules follow the shared modules -MODULES += \ - src \ - src/emucore \ - src/emucore/m6502 \ - src/common \ - src/controllers \ - src/os_dependent \ - src/games \ - src/environment \ - src/external - - -###################################################################### -# The build rules follow - normally you should have no need to -# touch whatever comes after here. -###################################################################### - -# Concat DEFINES and INCLUDES to form the CPPFLAGS -CPPFLAGS:= $(DEFINES) $(INCLUDES) - -# Include the build instructions for all modules --include $(addprefix $(srcdir)/, $(addsuffix /module.mk,$(MODULES))) - -# Depdir information -DEPDIRS = $(addsuffix /$(DEPDIR),$(MODULE_DIRS)) -DEPFILES = - -# The build rule for the Stella executable -$(EXECUTABLE): $(OBJS) - $(LD) $(LDFLAGS) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) $(PROF) -o $@ - -$(LIBRARY): $(OBJS) - $(LD) $(LDFLAGS) -shared -o $(LIBRARY) $(OBJS) $(LIBS) - -distclean: clean - $(RM_REC) $(DEPDIRS) - $(RM) build.rules config.h config.mak config.log - -clean: - $(RM) $(OBJS) $(EXECUTABLE) $(LIBRARY) - - - - -.PHONY: all clean dist distclean - -.SUFFIXES: .cxx -ifndef HAVE_GCC3 -# If you use GCC, disable the above and enable this for intelligent -# dependency tracking. -.cxx.o: - $(MKDIR) $(*D)/$(DEPDIR) - $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d2" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o - $(ECHO) "$(*D)/" > $(*D)/$(DEPDIR)/$(*F).d - $(CAT) "$(*D)/$(DEPDIR)/$(*F).d2" >> "$(*D)/$(DEPDIR)/$(*F).d" - $(RM) "$(*D)/$(DEPDIR)/$(*F).d2" - -.c.o: - $(MKDIR) $(*D)/$(DEPDIR) - $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d2" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o - $(ECHO) "$(*D)/" > $(*D)/$(DEPDIR)/$(*F).d - $(CAT) "$(*D)/$(DEPDIR)/$(*F).d2" >> "$(*D)/$(DEPDIR)/$(*F).d" - $(RM) "$(*D)/$(DEPDIR)/$(*F).d2" -else -# If you even have GCC 3.x, you can use this build rule, which is safer; the above -# rule can get you into a bad state if you Ctrl-C at the wrong moment. -# Also, with this GCC inserts additional dummy rules for the involved headers, -# which ensures a smooth compilation even if said headers become obsolete. -.cxx.o: - $(MKDIR) $(*D)/$(DEPDIR) - $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o - -.c.o: - $(MKDIR) $(*D)/$(DEPDIR) - $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o -endif - -ifdef HAVE_NASM -.SUFFIXES: .asm -.asm.o: - $(NASM) -O1 $(NASMFLAGS) -g -o $*.o $(<) -endif - -# Include the dependency tracking files. We add /dev/null at the end -# of the list to avoid a warning/error if no .d file exist --include $(wildcard $(addsuffix /*.d,$(DEPDIRS))) /dev/null - -# check if configure has been run or has been changed since last run -config.mak: $(srcdir)/configure - @echo "You need to run ./configure before you can run make" - @echo "Either you haven't run it before or it has changed." - @exit 1 - -install: all - $(INSTALL) -d "$(DESTDIR)$(BINDIR)" - $(INSTALL) -c -s -m 755 "$(srcdir)/stella$(EXEEXT)" "$(DESTDIR)$(BINDIR)/stella$(EXEEXT)" - $(INSTALL) -d "$(DESTDIR)$(DOCDIR)" - $(INSTALL) -c -m 644 "$(srcdir)/Announce.txt" "$(srcdir)/Changes.txt" "$(srcdir)/Copyright.txt" "$(srcdir)/License.txt" "$(srcdir)/README-SDL.txt" "$(srcdir)/Readme.txt" "$(srcdir)/Todo.txt" "$(srcdir)/docs/index.html" "$(srcdir)/docs/debugger.html" "$(DESTDIR)$(DOCDIR)/" - $(INSTALL) -d "$(DESTDIR)$(DOCDIR)/graphics" - $(INSTALL) -c -m 644 $(wildcard $(srcdir)/docs/graphics/*.png) "$(DESTDIR)$(DOCDIR)/graphics" - $(INSTALL) -d "$(DESTDIR)$(DATADIR)/applications" - $(INSTALL) -c -m 644 "$(srcdir)/src/unix/stella.desktop" "$(DESTDIR)$(DATADIR)/applications" - $(INSTALL) -d "$(DESTDIR)$(DATADIR)/icons" - $(INSTALL) -d "$(DESTDIR)$(DATADIR)/icons/mini" - $(INSTALL) -d "$(DESTDIR)$(DATADIR)/icons/large" - $(INSTALL) -c -m 644 "$(srcdir)/src/common/stella.png" "$(DESTDIR)$(DATADIR)/icons" - $(INSTALL) -c -m 644 "$(srcdir)/src/common/stella.png" "$(DESTDIR)$(DATADIR)/icons/mini" - $(INSTALL) -c -m 644 "$(srcdir)/src/common/stella.png" "$(DESTDIR)$(DATADIR)/icons/large" - -install-strip: install - strip stella$(EXEEXT) - -uninstall: - rm -f "$(DESTDIR)$(BINDIR)/stella$(EXEEXT)" - rm -rf "$(DESTDIR)$(DOCDIR)/" - rm -f "$(DESTDIR)$(DATADIR)/applications/stella.desktop" - rm -f "$(DESTDIR)$(DATADIR)/icons/stella.png" - rm -f "$(DESTDIR)$(DATADIR)/icons/mini/stella.png" - rm -f "$(DESTDIR)$(DATADIR)/icons/large/stella.png" - - -# Use Exuberant ctags (the one from Slackware's vim package, for instance), -# not the one from emacs! -tags: - ctags `find . -name '*.[ch]xx' -o -name '*.c' -o -name '*.y'` || true - - diff --git a/atari_py/ale_interface/makefile.unix b/atari_py/ale_interface/makefile.unix deleted file mode 100644 index 1367ba7..0000000 --- a/atari_py/ale_interface/makefile.unix +++ /dev/null @@ -1,246 +0,0 @@ -##============================================================================ -# makefile for the A.L.E: Atari Learing Environment (Powered by Stella) -##============================================================================ - -srcdir ?= . -# Set this to 1 to enable SDL, display_screen, and sound support -USE_SDL := 0 -# Set this to 1 to enable the RLGlue interface -USE_RLGLUE := 0 -DEFINES := -DRLGENV_NOMAINLOOP -LDFLAGS := -# /usr/local/include for RL-Glue -INCLUDES := -Isrc/controllers -Isrc/os_dependent -I/usr/include -I/usr/local/include -Isrc/environment -Isrc/external -LIBS_RLGLUE := -lrlutils -lrlgluenetdev -LIBS := -OBJS := -PROF := -MODULES := -MODULE_DIRS := -DISTNAME := atari_learning_environment -DEBUG := 0 - -# -------- Generated by configure ----------- -CXX := g++ -CXXFLAGS := -LD := g++ -LIBS += -lz -RANLIB := ranlib -INSTALL := install -AR := ar cru -MKDIR := mkdir -p -ECHO := printf -CAT := cat -RM := rm -f -RM_REC := rm -f -r -ZIP := zip -q -CP := cp - - -# MODULES += src/unix -MODULE_DIRS += -EXEEXT := -NASM := /usr/bin/nasm -NASMFLAGS := -f elf - -PREFIX := /usr/local -BINDIR := /usr/local/bin -DOCDIR := /usr/local/share/doc/stella -DATADIR := /usr/local/share -PROFILE := - -HAVE_GCC3 = 1 -HAVE_NASM = 1 - -INCLUDES += -Isrc/games -Isrc/emucore -Isrc/emucore/m6502/src -Isrc/emucore/m6502/src/bspf/src -Isrc/common -Isrc/controllers -Isrc/environment -OBJS += -DEFINES += -DUNIX -DHAS_ALTIVEC -DUSE_NASM -DBSPF_UNIX -DHAVE_INTTYPES -DWINDOWED_SUPPORT -DHAVE_GETTIMEOFDAY -DSNAPSHOT_SUPPORT -LDFLAGS += - -ifeq ($(strip $(USE_SDL)), 1) - CXXFLAGS+=$(shell sdl-config --cflags) - LIBS+=$(shell sdl-config --libs) - DEFINES += -D__USE_SDL -DSOUND_SUPPORT -endif - -ifeq ($(strip $(USE_RLGLUE)), 1) - DEFINES += -D__USE_RLGLUE - LIBS += $(LIBS_RLGLUE) -endif - - -# Uncomment this for stricter compile time code verification -# CXXFLAGS+= -Werror - -CXXFLAGS += -Wall -Wunused -fPIC - -ifeq ($(strip $(DEBUG)), 1) - CXXFLAGS += -g -O0 -else - CXXFLAGS += -O3 -endif - -ifdef PROFILE - PROF:= -g -pg -fprofile-arcs -ftest-coverage - CXXFLAGS+= $(PROF) -else - CXXFLAGS+= -fomit-frame-pointer -endif - -# Even more warnings... -#CXXFLAGS+= -pedantic -Wpointer-arith -Wcast-qual -Wconversion -#CXXFLAGS+= -Wshadow -Wimplicit -Wundef -Wnon-virtual-dtor -#CXXFLAGS+= -Wno-reorder -Wwrite-strings -fcheck-new -Wctor-dtor-privacy - -####################################################################### -# Misc stuff - you should never have to edit this # -####################################################################### - -EXECUTABLE := ale$(EXEEXT) -LIBRARY := libale.so - -all: tags $(EXECUTABLE) $(LIBRARY) - - -###################################################################### -# Various minor settings -###################################################################### - -# The name for the directory used for dependency tracking -DEPDIR := .deps - - -###################################################################### -# Module settings -###################################################################### - -MODULES := $(MODULES) - -# After the game specific modules follow the shared modules -MODULES += \ - src \ - src/emucore \ - src/emucore/m6502 \ - src/common \ - src/controllers \ - src/os_dependent \ - src/games \ - src/environment \ - src/external - - -###################################################################### -# The build rules follow - normally you should have no need to -# touch whatever comes after here. -###################################################################### - -# Concat DEFINES and INCLUDES to form the CPPFLAGS -CPPFLAGS:= $(DEFINES) $(INCLUDES) - -# Include the build instructions for all modules --include $(addprefix $(srcdir)/, $(addsuffix /module.mk,$(MODULES))) - -# Depdir information -DEPDIRS = $(addsuffix /$(DEPDIR),$(MODULE_DIRS)) -DEPFILES = - -# The build rule for the Stella executable -$(EXECUTABLE): $(OBJS) - $(LD) $(LDFLAGS) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) $(PROF) -o $@ - -$(LIBRARY): $(OBJS) - $(LD) $(LDFLAGS) -shared -o $(LIBRARY) $(OBJS) $(LIBS) - -distclean: clean - $(RM_REC) $(DEPDIRS) - $(RM) build.rules config.h config.mak config.log - -clean: - $(RM) $(OBJS) $(EXECUTABLE) $(LIBRARY) - - - - -.PHONY: all clean dist distclean - -.SUFFIXES: .cxx -ifndef HAVE_GCC3 -# If you use GCC, disable the above and enable this for intelligent -# dependency tracking. -.cxx.o: - $(MKDIR) $(*D)/$(DEPDIR) - $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d2" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o - $(ECHO) "$(*D)/" > $(*D)/$(DEPDIR)/$(*F).d - $(CAT) "$(*D)/$(DEPDIR)/$(*F).d2" >> "$(*D)/$(DEPDIR)/$(*F).d" - $(RM) "$(*D)/$(DEPDIR)/$(*F).d2" - -.c.o: - $(MKDIR) $(*D)/$(DEPDIR) - $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d2" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o - $(ECHO) "$(*D)/" > $(*D)/$(DEPDIR)/$(*F).d - $(CAT) "$(*D)/$(DEPDIR)/$(*F).d2" >> "$(*D)/$(DEPDIR)/$(*F).d" - $(RM) "$(*D)/$(DEPDIR)/$(*F).d2" -else -# If you even have GCC 3.x, you can use this build rule, which is safer; the above -# rule can get you into a bad state if you Ctrl-C at the wrong moment. -# Also, with this GCC inserts additional dummy rules for the involved headers, -# which ensures a smooth compilation even if said headers become obsolete. -.cxx.o: - $(MKDIR) $(*D)/$(DEPDIR) - $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o - -.c.o: - $(MKDIR) $(*D)/$(DEPDIR) - $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o -endif - -ifdef HAVE_NASM -.SUFFIXES: .asm -.asm.o: - $(NASM) -O1 $(NASMFLAGS) -g -o $*.o $(<) -endif - -# Include the dependency tracking files. We add /dev/null at the end -# of the list to avoid a warning/error if no .d file exist --include $(wildcard $(addsuffix /*.d,$(DEPDIRS))) /dev/null - -# check if configure has been run or has been changed since last run -config.mak: $(srcdir)/configure - @echo "You need to run ./configure before you can run make" - @echo "Either you haven't run it before or it has changed." - @exit 1 - -install: all - $(INSTALL) -d "$(DESTDIR)$(BINDIR)" - $(INSTALL) -c -s -m 755 "$(srcdir)/stella$(EXEEXT)" "$(DESTDIR)$(BINDIR)/stella$(EXEEXT)" - $(INSTALL) -d "$(DESTDIR)$(DOCDIR)" - $(INSTALL) -c -m 644 "$(srcdir)/Announce.txt" "$(srcdir)/Changes.txt" "$(srcdir)/Copyright.txt" "$(srcdir)/License.txt" "$(srcdir)/README-SDL.txt" "$(srcdir)/Readme.txt" "$(srcdir)/Todo.txt" "$(srcdir)/docs/index.html" "$(srcdir)/docs/debugger.html" "$(DESTDIR)$(DOCDIR)/" - $(INSTALL) -d "$(DESTDIR)$(DOCDIR)/graphics" - $(INSTALL) -c -m 644 $(wildcard $(srcdir)/docs/graphics/*.png) "$(DESTDIR)$(DOCDIR)/graphics" - $(INSTALL) -d "$(DESTDIR)$(DATADIR)/applications" - $(INSTALL) -c -m 644 "$(srcdir)/src/unix/stella.desktop" "$(DESTDIR)$(DATADIR)/applications" - $(INSTALL) -d "$(DESTDIR)$(DATADIR)/icons" - $(INSTALL) -d "$(DESTDIR)$(DATADIR)/icons/mini" - $(INSTALL) -d "$(DESTDIR)$(DATADIR)/icons/large" - $(INSTALL) -c -m 644 "$(srcdir)/src/common/stella.png" "$(DESTDIR)$(DATADIR)/icons" - $(INSTALL) -c -m 644 "$(srcdir)/src/common/stella.png" "$(DESTDIR)$(DATADIR)/icons/mini" - $(INSTALL) -c -m 644 "$(srcdir)/src/common/stella.png" "$(DESTDIR)$(DATADIR)/icons/large" - -install-strip: install - strip stella$(EXEEXT) - -uninstall: - rm -f "$(DESTDIR)$(BINDIR)/stella$(EXEEXT)" - rm -rf "$(DESTDIR)$(DOCDIR)/" - rm -f "$(DESTDIR)$(DATADIR)/applications/stella.desktop" - rm -f "$(DESTDIR)$(DATADIR)/icons/stella.png" - rm -f "$(DESTDIR)$(DATADIR)/icons/mini/stella.png" - rm -f "$(DESTDIR)$(DATADIR)/icons/large/stella.png" - - -# Use Exuberant ctags (the one from Slackware's vim package, for instance), -# not the one from emacs! -tags: - ctags `find . -name '*.[ch]xx' -o -name '*.c' -o -name '*.y'` || true - - diff --git a/atari_py/ale_interface/setup.sh b/atari_py/ale_interface/setup.sh deleted file mode 100755 index 2d66814..0000000 --- a/atari_py/ale_interface/setup.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -set -e -mkdir -p build && cd build && cmake .. && make -j4 diff --git a/atari_py/ale_interface/src/ale_interface.cpp b/atari_py/ale_interface/src/ale_interface.cpp index f8c81b1..15310af 100644 --- a/atari_py/ale_interface/src/ale_interface.cpp +++ b/atari_py/ale_interface/src/ale_interface.cpp @@ -27,34 +27,50 @@ * * The shared library interface. **************************************************************************** */ + #include "ale_interface.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include + +#include "common/ColourPalette.hpp" +#include "common/Constants.h" +#include "emucore/Console.hxx" +#include "emucore/Props.hxx" +#include "environment/ale_screen.hpp" +#include "games/RomSettings.hpp" -using namespace std; using namespace ale; + // Display ALE welcome message std::string ALEInterface::welcomeMessage() { std::ostringstream oss; - oss << "A.L.E: Arcade Learning Environment (version " - << Version << ")\n" - << "[Powered by Stella]\n" - << "Use -help for help screen."; + oss << "A.L.E: Arcade Learning Environment (version " << Version << ")\n" + << "[Powered by Stella]\n" << "Use -help for help screen."; return oss.str(); } void ALEInterface::disableBufferedIO() { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, 0); - cin.rdbuf()->pubsetbuf(0,0); - cout.rdbuf()->pubsetbuf(0,0); - cin.sync_with_stdio(); - cout.sync_with_stdio(); + std::cin.rdbuf()->pubsetbuf(0, 0); + std::cout.rdbuf()->pubsetbuf(0, 0); + std::cin.sync_with_stdio(); + std::cout.sync_with_stdio(); } -void ALEInterface::createOSystem(std::auto_ptr &theOSystem, - std::auto_ptr &theSettings) { -#if (defined(WIN32) || defined(__MINGW32__)) +void ALEInterface::createOSystem(scoped_ptr& theOSystem, + scoped_ptr& theSettings) { +#if (defined(_WIN32) || defined(__MINGW32__)) theOSystem.reset(new OSystemWin32()); theSettings.reset(new SettingsWin32(theOSystem.get())); #else @@ -62,48 +78,67 @@ void ALEInterface::createOSystem(std::auto_ptr &theOSystem, theSettings.reset(new SettingsUNIX(theOSystem.get())); #endif - setDefaultSettings(theOSystem->settings()); - theOSystem->settings().loadConfig(); } -void ALEInterface::loadSettings(const string& romfile, - std::auto_ptr &theOSystem) { +void ALEInterface::checkForUnsupportedRom(OSystem& theOSystem) { + const Properties properties = theOSystem.console().properties(); + const std::string md5 = properties.get(Cartridge_MD5); + bool found = false; + std::ifstream ss("md5.txt"); + std::string item; + while (!found && std::getline(ss, item)) { + if (!item.compare(0, md5.size(), md5)) { + const std::string rom_candidate = item.substr(md5.size() + 1); + found = true; + } + } + if (!found) { + // If the md5 doesn't match our master list, warn the user. + Logger::Warning << std::endl; + Logger::Warning << "WARNING: Possibly unsupported ROM: mismatched MD5." << std::endl; + Logger::Warning << "Cartridge_MD5: " << md5 << std::endl; + const std::string name = properties.get(Cartridge_Name); + Logger::Warning << "Cartridge_name: " << name << std::endl; + Logger::Warning << std::endl; + } +} + +void ALEInterface::loadSettings(const std::string& rom, const std::string& name, + OSystem& theOSystem) { // Load the configuration from a config file (passed on the command // line), if provided - string configFile = theOSystem->settings().getString("config", false); + std::string configFile = theOSystem.settings().getString("config", false); - if (!configFile.empty()) - theOSystem->settings().loadConfig(configFile.c_str()); + if (!configFile.empty()) { + theOSystem.settings().loadConfig(configFile.c_str()); + } - theOSystem->settings().validate(); - theOSystem->create(); + theOSystem.settings().validate(); + theOSystem.create(); // Attempt to load the ROM - if (romfile == "" || !FilesystemNode::fileExists(romfile)) { - Logger::Error << "No ROM File specified or the ROM file was not found." + if (rom.empty()) { + Logger::Error << "Empty ROM File specified" << std::endl; exit(1); - } else if (theOSystem->createConsole(romfile)) { - Logger::Info << "Running ROM file..." << std::endl; - theOSystem->settings().setString("rom_file", romfile); + } else if (theOSystem.createConsole(rom, name)) { + checkForUnsupportedRom(theOSystem); + Logger::Info << "Running ROM " << name << "..." << std::endl; + theOSystem.settings().setString("rom_name", name); } else { + Logger::Error << "Unable to create console for " << name << std::endl; exit(1); } - // Seed random number generator - if (theOSystem->settings().getString("random_seed") == "time") { - Logger::Info << "Random Seed: Time" << endl; - Random::seed((uInt32)time(NULL)); - } else { - int seed = theOSystem->settings().getInt("random_seed"); - assert(seed >= 0); - Logger::Info << "Random Seed: " << seed << endl; - Random::seed((uInt32)seed); - } +// Must force the resetting of the OSystem's random seed, which is set before we change +// choose our random seed. + Logger::Info << "Random seed is " + << theOSystem.settings().getInt("random_seed") << std::endl; + theOSystem.resetRNGSeed(); - string currentDisplayFormat = theOSystem->console().getFormat(); - theOSystem->colourPalette().setPalette("standard", currentDisplayFormat); + std::string currentDisplayFormat = theOSystem.console().getFormat(); + theOSystem.colourPalette().setPalette("standard", currentDisplayFormat); } ALEInterface::ALEInterface() { @@ -119,30 +154,34 @@ ALEInterface::ALEInterface(bool display_screen) { this->setBool("display_screen", display_screen); } -ALEInterface::~ALEInterface() {} +ALEInterface::~ALEInterface() { +} // Loads and initializes a game. After this call the game should be // ready to play. Resets the OSystem/Console/Environment/etc. This is // necessary after changing a setting. Optionally specify a new rom to // load. -void ALEInterface::loadROM(string rom_file = "") { +bool ALEInterface::loadROM(std::string rom_file, std::string name) { assert(theOSystem.get()); - if (rom_file.empty()) { - rom_file = theOSystem->romFile(); - } - loadSettings(rom_file, theOSystem); - romSettings.reset(buildRomRLWrapper(rom_file)); + loadSettings(rom_file, name, *theOSystem); + romSettings.reset(buildRomRLWrapper(name)); + if (romSettings.get() == NULL) return false; environment.reset(new StellaEnvironment(theOSystem.get(), romSettings.get())); max_num_frames = theOSystem->settings().getInt("max_num_frames_per_episode"); environment->reset(); #ifndef __USE_SDL if (theOSystem->p_display_screen != NULL) { - Logger::Error << "Screen display requires directive __USE_SDL to be defined." << endl; - Logger::Error << "Please recompile this code with flag '-D__USE_SDL'." << endl; - Logger::Error << "Also ensure ALE has been compiled with USE_SDL active (see ALE makefile)." << endl; + Logger::Error + << "Screen display requires directive __USE_SDL to be defined." << std::endl; + Logger::Error << "Please recompile this code with flag '-D__USE_SDL'." + << std::endl; + Logger::Error + << "Also ensure ALE has been compiled with USE_SDL active (see ALE makefile)." + << std::endl; exit(1); } #endif + return true; } // Get the value of a setting. @@ -164,46 +203,44 @@ float ALEInterface::getFloat(const std::string& key) { } // Set the value of a setting. -void ALEInterface::setString(const string& key, const string& value) { +void ALEInterface::setString(const std::string& key, const std::string& value) { assert(theSettings.get()); assert(theOSystem.get()); theSettings->setString(key, value); theSettings->validate(); } -void ALEInterface::setInt(const string& key, const int value) { +void ALEInterface::setInt(const std::string& key, const int value) { assert(theSettings.get()); assert(theOSystem.get()); theSettings->setInt(key, value); theSettings->validate(); } -void ALEInterface::setBool(const string& key, const bool value) { +void ALEInterface::setBool(const std::string& key, const bool value) { assert(theSettings.get()); assert(theOSystem.get()); theSettings->setBool(key, value); theSettings->validate(); } -void ALEInterface::setFloat(const string& key, const float value) { +void ALEInterface::setFloat(const std::string& key, const float value) { assert(theSettings.get()); assert(theOSystem.get()); theSettings->setFloat(key, value); theSettings->validate(); } - // Resets the game, but not the full system. void ALEInterface::reset_game() { environment->reset(); } // Indicates if the game has ended. -bool ALEInterface::game_over() { - return (environment->isTerminal() || - (max_num_frames > 0 && getEpisodeFrameNumber() >= max_num_frames)); +bool ALEInterface::game_over() const { + return environment->isTerminal(); } // The remaining number of lives. -const int ALEInterface::lives() { - if (!romSettings.get()){ +int ALEInterface::lives() { + if (!romSettings.get()) { throw std::runtime_error("ROM not set"); } return romSettings->lives(); @@ -226,10 +263,47 @@ reward_t ALEInterface::act(Action action) { return reward; } +// Returns the vector of modes available for the current game. +// This should be called only after the rom is loaded. +ModeVect ALEInterface::getAvailableModes() { + return romSettings->getAvailableModes(); +} + +// Sets the mode of the game. +// The mode must be an available mode. +// This should be called only after the rom is loaded. +void ALEInterface::setMode(game_mode_t m) { + //We first need to make sure m is an available mode + ModeVect available = romSettings->getAvailableModes(); + if(find(available.begin(), available.end(), m) != available.end()) { + environment->setMode(m); + } else { + throw std::runtime_error("Invalid game mode requested"); + } +} + +//Returns the vector of difficulties available for the current game. +//This should be called only after the rom is loaded. +DifficultyVect ALEInterface::getAvailableDifficulties() { + return romSettings->getAvailableDifficulties(); +} + +// Sets the difficulty of the game. +// The difficulty must be an available mode. +// This should be called only after the rom is loaded. +void ALEInterface::setDifficulty(difficulty_t m) { + DifficultyVect available = romSettings->getAvailableDifficulties(); + if(find(available.begin(), available.end(), m) != available.end()) { + environment->setDifficulty(m); + } else { + throw std::runtime_error("Invalid difficulty requested"); + } +} + // Returns the vector of legal actions. This should be called only // after the rom is loaded. ActionVect ALEInterface::getLegalActionSet() { - if (!romSettings.get()){ + if (!romSettings.get()) { throw std::runtime_error("ROM not set"); } return romSettings->getAllActions(); @@ -238,7 +312,7 @@ ActionVect ALEInterface::getLegalActionSet() { // Returns the vector of the minimal set of actions needed to play // the game. ActionVect ALEInterface::getMinimalActionSet() { - if (!romSettings.get()){ + if (!romSettings.get()) { throw std::runtime_error("ROM not set"); } return romSettings->getMinimalActionSet(); @@ -250,7 +324,7 @@ int ALEInterface::getFrameNumber() { } // Returns the frame number since the start of the current episode -int ALEInterface::getEpisodeFrameNumber() { +int ALEInterface::getEpisodeFrameNumber() const { return environment->getEpisodeFrameNumber(); } @@ -259,6 +333,32 @@ const ALEScreen& ALEInterface::getScreen() { return environment->getScreen(); } +//This method should receive an empty vector to fill it with +//the grayscale colours +void ALEInterface::getScreenGrayscale( + std::vector& grayscale_output_buffer) { + size_t w = environment->getScreen().width(); + size_t h = environment->getScreen().height(); + size_t screen_size = w * h; + + pixel_t *ale_screen_data = environment->getScreen().getArray(); + theOSystem->colourPalette().applyPaletteGrayscale(grayscale_output_buffer, + ale_screen_data, screen_size); +} + +//This method should receive a vector to fill it with +//the RGB colours. The first positions contain the red colours, +//followed by the green colours and then the blue colours +void ALEInterface::getScreenRGB(std::vector& output_rgb_buffer) { + size_t w = environment->getScreen().width(); + size_t h = environment->getScreen().height(); + size_t screen_size = w * h; + + pixel_t *ale_screen_data = environment->getScreen().getArray(); + + theOSystem->colourPalette().applyPaletteRGB(output_rgb_buffer, ale_screen_data, screen_size); +} + // Returns the current RAM content const ALERAM& ALEInterface::getRAM() { return environment->getRAM(); @@ -282,12 +382,22 @@ void ALEInterface::restoreState(const ALEState& state) { return environment->restoreState(state); } -void ALEInterface::saveScreenPNG(const string& filename) { - +ALEState ALEInterface::cloneSystemState() { + return environment->cloneSystemState(); +} + +void ALEInterface::restoreSystemState(const ALEState& state) { + return environment->restoreSystemState(state); +} + +/* Kojoley +void ALEInterface::saveScreenPNG(const std::string& filename) { ScreenExporter exporter(theOSystem->colourPalette()); exporter.save(environment->getScreen(), filename); } -ScreenExporter *ALEInterface::createScreenExporter(const std::string &filename) const { - return new ScreenExporter(theOSystem->colourPalette(), filename); +ScreenExporter *ALEInterface::createScreenExporter( + const std::string &filename) const { + return new ScreenExporter(theOSystem->colourPalette(), filename); } +Kojoley */ diff --git a/atari_py/ale_interface/src/ale_interface.hpp b/atari_py/ale_interface/src/ale_interface.hpp index 47a5f7a..9d03f7d 100644 --- a/atari_py/ale_interface/src/ale_interface.hpp +++ b/atari_py/ale_interface/src/ale_interface.hpp @@ -32,21 +32,23 @@ #include "emucore/FSNode.hxx" #include "emucore/OSystem.hxx" -#include "os_dependent/SettingsWin32.hxx" -#include "os_dependent/OSystemWin32.hxx" -#include "os_dependent/SettingsUNIX.hxx" -#include "os_dependent/OSystemUNIX.hxx" +#if (defined(_WIN32) || defined(__MINGW32__)) +# include "os_dependent/SettingsWin32.hxx" +# include "os_dependent/OSystemWin32.hxx" +#else +# include "os_dependent/SettingsUNIX.hxx" +# include "os_dependent/OSystemUNIX.hxx" +#endif #include "games/Roms.hpp" -#include "common/Defaults.hpp" #include "common/display_screen.h" #include "environment/stella_environment.hpp" -#include "common/ScreenExporter.hpp" +//Kojoley #include "common/ScreenExporter.hpp" #include "common/Log.hpp" #include -#include +#include "common/scoped_ptr.hpp" -static const std::string Version = "0.5.0"; +static const std::string Version = "0.6.0"; /** This class interfaces ALE with external code for controlling agents. @@ -74,7 +76,7 @@ class ALEInterface { // Resets the Atari and loads a game. After this call the game // should be ready to play. This is necessary after changing a // setting for the setting to take effect. - void loadROM(std::string rom_file); + bool loadROM(std::string rom = "", std::string name = ""); // Applies an action to the game and returns the reward. It is the // user's responsibility to check if the game has ended and reset @@ -83,11 +85,38 @@ class ALEInterface { reward_t act(Action action); // Indicates if the game has ended. - bool game_over(); + bool game_over() const; // Resets the game, but not the full system. void reset_game(); + // Returns the vector of modes available for the current game. + // This should be called only after the rom is loaded. + ModeVect getAvailableModes(); + + // Sets the mode of the game. + // The mode must be an available mode (otherwise it throws an exception). + // This should be called only after the rom is loaded. + void setMode(game_mode_t m); + + //Returns the vector of difficulties available for the current game. + //This should be called only after the rom is loaded. Notice + // that there are 2 levers, the right and left switches. They + // are not tied to any specific player. In Venture, for example, + // we have the following interpretation for the difficulties: + // Skill Switch + // Level Setting + // 1 left B/right B + // 2 left B/right A + // 3 left A/right B + // 4 left A/right A + DifficultyVect getAvailableDifficulties(); + + // Sets the difficulty of the game. + // The difficulty must be an available mode (otherwise it throws an exception). + // This should be called only after the rom is loaded. + void setDifficulty(difficulty_t m); + // Returns the vector of legal actions. This should be called only // after the rom is loaded. ActionVect getLegalActionSet(); @@ -100,14 +129,23 @@ class ALEInterface { int getFrameNumber(); // The remaining number of lives. - const int lives(); + int lives(); // Returns the frame number since the start of the current episode - int getEpisodeFrameNumber(); + int getEpisodeFrameNumber() const; // Returns the current game screen const ALEScreen &getScreen(); + //This method should receive an empty vector to fill it with + //the grayscale colours + void getScreenGrayscale(std::vector& grayscale_output_buffer); + + //This method should receive a vector to fill it with + //the RGB colours. The first positions contain the red colours, + //followed by the green colours and then the blue colours + void getScreenRGB(std::vector& output_rgb_buffer); + // Returns the current RAM content const ALERAM &getRAM(); @@ -117,33 +155,49 @@ class ALEInterface { // Loads the state of the system void loadState(); + // This makes a copy of the environment state. This copy does *not* include pseudorandomness, + // making it suitable for planning purposes. By contrast, see cloneSystemState. ALEState cloneState(); + // Reverse operation of cloneState(). This does not restore pseudorandomness, so that repeated + // calls to restoreState() in the stochastic controls setting will not lead to the same outcomes. + // By contrast, see restoreSystemState. void restoreState(const ALEState& state); + // This makes a copy of the system & environment state, suitable for serialization. This includes + // pseudorandomness and so is *not* suitable for planning purposes. + ALEState cloneSystemState(); + + // Reverse operation of cloneSystemState. + void restoreSystemState(const ALEState& state); + // Save the current screen as a png file - void saveScreenPNG(const std::string& filename); + //Kojoley void saveScreenPNG(const std::string& filename); // Creates a ScreenExporter object which can be used to save a sequence of frames. Ownership // said object is passed to the caller. Frames are saved in the directory 'path', which needs // to exists. - ScreenExporter *createScreenExporter(const std::string &path) const; + //Kojoley ScreenExporter *createScreenExporter(const std::string &path) const; public: - std::auto_ptr theOSystem; - std::auto_ptr theSettings; - std::auto_ptr romSettings; - std::auto_ptr environment; + scoped_ptr theOSystem; + scoped_ptr theSettings; + scoped_ptr romSettings; + scoped_ptr environment; int max_num_frames; // Maximum number of frames for each episode public: // Display ALE welcome message static std::string welcomeMessage(); static void disableBufferedIO(); - static void createOSystem(std::auto_ptr &theOSystem, - std::auto_ptr &theSettings); + static void createOSystem(scoped_ptr& theOSystem, + scoped_ptr& theSettings); static void loadSettings(const std::string& romfile, - std::auto_ptr &theOSystem); + const std::string& name, + OSystem& theOSystem); + + private: + static void checkForUnsupportedRom(OSystem& theOSystem); }; #endif diff --git a/atari_py/ale_interface/src/common/ColourPalette.cpp b/atari_py/ale_interface/src/common/ColourPalette.cpp index 1244c52..fde02a0 100644 --- a/atari_py/ale_interface/src/common/ColourPalette.cpp +++ b/atari_py/ale_interface/src/common/ColourPalette.cpp @@ -34,7 +34,10 @@ inline uInt32 convertGrayscale(uInt32 packedRGBValue) double g = (packedRGBValue >> 8) & 0xff; double b = (packedRGBValue >> 0) & 0xff; - uInt8 lum = (uInt8) round(r * 0.2989 + g * 0.5870 + b * 0.1140 ); + // `round` is the part of c++11, but we have to support c++98 + // because of python2. As we have unsigend values here it is safe + // to replace `round` call with +0.5 which is equal in this case. + uInt8 lum = (uInt8) (r * 0.2989 + g * 0.5870 + b * 0.1140 + 0.5); return packRGB(lum, lum, lum); } @@ -45,8 +48,10 @@ ColourPalette::ColourPalette(): m_palette(NULL) { void ColourPalette::getRGB(int val, int &r, int &g, int &b) const { - assert (m_palette != NULL); + assert(m_palette != NULL); assert(val >= 0 && val <= 0xFF); + // Make sure we are reading from RGB, not grayscale. + assert((val & 0x01) == 0); // Set the RGB components accordingly r = (m_palette[val] >> 16) & 0xFF; @@ -56,9 +61,10 @@ void ColourPalette::getRGB(int val, int &r, int &g, int &b) const uInt8 ColourPalette::getGrayscale(int val) const { - assert (m_palette != NULL); + assert(m_palette != NULL); assert(val >= 0 && val < 0xFF); - + assert((val & 0x01) == 1); + // Set the RGB components accordingly return (m_palette[val+1] >> 0) & 0xFF; } @@ -81,6 +87,21 @@ void ColourPalette::applyPaletteRGB(uInt8* dst_buffer, uInt8 *src_buffer, size_t } } +void ColourPalette::applyPaletteRGB(std::vector& dst_buffer, uInt8 *src_buffer, size_t src_size) +{ + dst_buffer.resize(3 * src_size); + assert(dst_buffer.size() == 3 * src_size); + + uInt8 *p = src_buffer; + + for(size_t i = 0; i < src_size * 3; i += 3, p++){ + int rgb = m_palette[*p]; + dst_buffer[i+0] = (unsigned char) ((rgb >> 16)); // r + dst_buffer[i+1] = (unsigned char) ((rgb >> 8)); // g + dst_buffer[i+2] = (unsigned char) ((rgb >> 0)); // b + } +} + void ColourPalette::applyPaletteGrayscale(uInt8* dst_buffer, uInt8 *src_buffer, size_t src_size) { uInt8 *p = src_buffer; @@ -91,6 +112,18 @@ void ColourPalette::applyPaletteGrayscale(uInt8* dst_buffer, uInt8 *src_buffer, } } +void ColourPalette::applyPaletteGrayscale(std::vector& dst_buffer, uInt8 *src_buffer, size_t src_size) +{ + dst_buffer.resize(src_size); + assert(dst_buffer.size() == src_size); + + uInt8 *p = src_buffer; + + for(size_t i = 0; i < src_size; i++, p++){ + dst_buffer[i] = (unsigned char) (m_palette[*p+1] & 0xFF); + } +} + void ColourPalette::setPalette(const string& type, const string& displayFormat) { diff --git a/atari_py/ale_interface/src/common/ColourPalette.hpp b/atari_py/ale_interface/src/common/ColourPalette.hpp index b9e4587..f769955 100644 --- a/atari_py/ale_interface/src/common/ColourPalette.hpp +++ b/atari_py/ale_interface/src/common/ColourPalette.hpp @@ -16,6 +16,7 @@ #ifndef __COLOUR_PALETTE_HPP__ #define __COLOUR_PALETTE_HPP__ +#include #include // Include obscure header file for uInt32 definition #include "../emucore/m6502/src/bspf/src/bspf.hxx" @@ -41,6 +42,7 @@ class ColourPalette { 8 bits => 24 bits */ void applyPaletteRGB(uInt8* dst_buffer, uInt8 *src_buffer, size_t src_size); + void applyPaletteRGB(std::vector& dst_buffer, uInt8 *src_buffer, size_t src_size); /** Applies the current grayscale palette to the src_buffer and returns the results in dst_buffer @@ -48,6 +50,7 @@ class ColourPalette { 8 bits => 8 bits */ void applyPaletteGrayscale(uInt8* dst_buffer, uInt8 *src_buffer, size_t src_size); + void applyPaletteGrayscale(std::vector& dst_buffer, uInt8 *src_buffer, size_t src_size); /** Loads all defined palettes with PAL color-loss data depending diff --git a/atari_py/ale_interface/src/common/Constants.h b/atari_py/ale_interface/src/common/Constants.h index 0918c2a..3a37fa3 100644 --- a/atari_py/ale_interface/src/common/Constants.h +++ b/atari_py/ale_interface/src/common/Constants.h @@ -79,6 +79,14 @@ std::string action_to_string(Action a); // Define datatypes typedef std::vector ActionVect; +// mode type +typedef unsigned game_mode_t; +typedef std::vector ModeVect; + +// difficulty type +typedef unsigned difficulty_t; +typedef std::vector DifficultyVect; + // reward type for RL interface typedef int reward_t; diff --git a/atari_py/ale_interface/src/common/Defaults.cpp b/atari_py/ale_interface/src/common/Defaults.cpp deleted file mode 100644 index ff506fc..0000000 --- a/atari_py/ale_interface/src/common/Defaults.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* ***************************************************************************** - * A.L.E (Arcade Learning Environment) - * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and - * the Reinforcement Learning and Artificial Intelligence Laboratory - * Released under the GNU General Public License; see License.txt for details. - * - * Based on: Stella -- "An Atari 2600 VCS Emulator" - * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team - * - * ***************************************************************************** - * Defaults.cpp - * - * Defines methods for setting default parameters. - * - **************************************************************************** */ -#include "Defaults.hpp" - -void setDefaultSettings(Settings &settings) { - // Controller settings - settings.setInt("max_num_frames", 0); - settings.setInt("max_num_frames_per_episode", 0); - - // FIFO controller settings - settings.setBool("run_length_encoding", true); - - // Environment customization settings - settings.setBool("restricted_action_set", false); - settings.setString("random_seed", "time"); - settings.setBool("color_averaging", false); - settings.setBool("send_rgb", false); - settings.setInt("frame_skip", 1); - settings.setFloat("repeat_action_probability", 0.25); - - // Display Settings - settings.setBool("display_screen", false); - - // Record settings - settings.setString("record_sound_filename", ""); -} diff --git a/atari_py/ale_interface/src/common/Defaults.hpp b/atari_py/ale_interface/src/common/Defaults.hpp deleted file mode 100644 index c9bca20..0000000 --- a/atari_py/ale_interface/src/common/Defaults.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/* ***************************************************************************** - * A.L.E (Arcade Learning Environment) - * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and - * the Reinforcement Learning and Artificial Intelligence Laboratory - * Released under the GNU General Public License; see License.txt for details. - * - * Based on: Stella -- "An Atari 2600 VCS Emulator" - * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team - * - * ***************************************************************************** - * Defaults.hpp - * - * Defines methods for setting default parameters. - * - **************************************************************************** */ - -#ifndef __DEFAULTS_HPP__ -#define __DEFAULTS_HPP__ - -#include "../emucore/Settings.hxx" - -/** Sets all of the ALE-specific default settings */ -void setDefaultSettings(Settings &settings); - -#endif // __DEFAULTS_HPP__ diff --git a/atari_py/ale_interface/src/common/Log.cpp b/atari_py/ale_interface/src/common/Log.cpp index 901f0f5..64ef888 100644 --- a/atari_py/ale_interface/src/common/Log.cpp +++ b/atari_py/ale_interface/src/common/Log.cpp @@ -2,7 +2,7 @@ #include using namespace ale; -Logger::mode Logger::current_mode = Warning; +Logger::mode Logger::current_mode = Info; void Logger::setMode(Logger::mode m){ current_mode = m; diff --git a/atari_py/ale_interface/src/common/ScreenExporter.cpp b/atari_py/ale_interface/src/common/ScreenExporter.cpp deleted file mode 100644 index c2fb30a..0000000 --- a/atari_py/ale_interface/src/common/ScreenExporter.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* ***************************************************************************** - * A.L.E (Arcade Learning Environment) - * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and - * the Reinforcement Learning and Artificial Intelligence Laboratory - * Released under the GNU General Public License; see License.txt for details. - * - * Based on: Stella -- "An Atari 2600 VCS Emulator" - * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team - * - * ***************************************************************************** - * ScreenExporter.hpp - * - * A class for exporting Atari 2600 frames as PNGs. - * - **************************************************************************** */ - -#include "ScreenExporter.hpp" -#include -#include -#include -#include "Log.hpp" - -// MGB: These methods originally belonged to ExportScreen. Possibly these should be returned to -// their own class, rather than be static methods. They are here to avoid exposing the gritty -// details of PNG generation. -static void writePNGChunk(std::ofstream& out, const char* type, uInt8* data, int size) { - - // Stuff the length/type into the buffer - uInt8 temp[8]; - temp[0] = size >> 24; - temp[1] = size >> 16; - temp[2] = size >> 8; - temp[3] = size; - temp[4] = type[0]; - temp[5] = type[1]; - temp[6] = type[2]; - temp[7] = type[3]; - - // Write the header - out.write((const char*)temp, 8); - - // Append the actual data - uInt32 crc = crc32(0, temp + 4, 4); - if(size > 0) - { - out.write((const char*)data, size); - crc = crc32(crc, data, size); - } - - // Write the CRC - temp[0] = crc >> 24; - temp[1] = crc >> 16; - temp[2] = crc >> 8; - temp[3] = crc; - out.write((const char*)temp, 4); -} - - -static void writePNGHeader(std::ofstream& out, const ALEScreen &screen, bool doubleWidth = true) { - - int width = doubleWidth ? screen.width() * 2: screen.width(); - int height = screen.height(); - // PNG file header - uInt8 header[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - out.write((const char*)header, sizeof(header)); - - // PNG IHDR - uInt8 ihdr[13]; - ihdr[0] = (width >> 24) & 0xFF; // width - ihdr[1] = (width >> 16) & 0xFF; - ihdr[2] = (width >> 8) & 0xFF; - ihdr[3] = (width >> 0) & 0xFF; - ihdr[4] = (height >> 24) & 0xFF; // height - ihdr[5] = (height >> 16) & 0xFF; - ihdr[6] = (height >> 8) & 0xFF; - ihdr[7] = (height >> 0) & 0xFF; - ihdr[8] = 8; // 8 bits per sample (24 bits per pixel) - ihdr[9] = 2; // PNG_COLOR_TYPE_RGB - ihdr[10] = 0; // PNG_COMPRESSION_TYPE_DEFAULT - ihdr[11] = 0; // PNG_FILTER_TYPE_DEFAULT - ihdr[12] = 0; // PNG_INTERLACE_NONE - writePNGChunk(out, "IHDR", ihdr, sizeof(ihdr)); -} - - -static void writePNGData(std::ofstream &out, const ALEScreen &screen, const ColourPalette &palette, bool doubleWidth = true) { - - int dataWidth = screen.width(); - int width = doubleWidth ? dataWidth * 2 : dataWidth; - int height = screen.height(); - - // If so desired, double the width - - // Fill the buffer with scanline data - int rowbytes = width * 3; - - std::vector buffer((rowbytes + 1) * height, 0); - uInt8* buf_ptr = &buffer[0]; - - for(int i = 0; i < height; i++) { - *buf_ptr++ = 0; // first byte of row is filter type - for(int j = 0; j < dataWidth; j++) { - int r, g, b; - - palette.getRGB(screen.getArray()[i * dataWidth + j], r, g, b); - // Double the pixel width, if so desired - int jj = doubleWidth ? 2 * j : j; - - buf_ptr[jj * 3 + 0] = r; - buf_ptr[jj * 3 + 1] = g; - buf_ptr[jj * 3 + 2] = b; - - if (doubleWidth) { - - jj = jj + 1; - - buf_ptr[jj * 3 + 0] = r; - buf_ptr[jj * 3 + 1] = g; - buf_ptr[jj * 3 + 2] = b; - } - } - buf_ptr += rowbytes; // add pitch - } - - // Compress the data with zlib - uLongf compmemsize = (uLongf)((height * (width + 1) * 3 + 1) + 12); - std::vector compmem(compmemsize, 0); - - if((compress(&compmem[0], &compmemsize, &buffer[0], height * (width * 3 + 1)) != Z_OK)) { - - // @todo -- throw a proper exception - ale::Logger::Error << "Error: Couldn't compress PNG" << std::endl; - return; - } - - // Write the compressed framebuffer data - writePNGChunk(out, "IDAT", &compmem[0], compmemsize); -} - - -static void writePNGEnd(std::ofstream &out) { - - // Finish up - writePNGChunk(out, "IEND", 0, 0); -} - -ScreenExporter::ScreenExporter(ColourPalette &palette): - m_palette(palette), - m_frame_number(0), - m_frame_field_width(6) { -} - - -ScreenExporter::ScreenExporter(ColourPalette &palette, const std::string &path): - m_palette(palette), - m_frame_number(0), - m_frame_field_width(6), - m_path(path) { -} - - -void ScreenExporter::save(const ALEScreen &screen, const std::string &filename) const { - - // Open file for writing - std::ofstream out(filename.c_str(), std::ios_base::binary); - if (!out.good()) { - - // @todo exception - ale::Logger::Error << "Could not open " << filename << " for writing" << std::endl; - return; - } - - // Now write the PNG proper - writePNGHeader(out, screen, true); - writePNGData(out, screen, m_palette, true); - writePNGEnd(out); - - out.close(); -} - -void ScreenExporter::saveNext(const ALEScreen &screen) { - - // Must have specified a directory. - assert(m_path.size() > 0); - - // MGB: It would be nice here to automagically create paths, but the only way I know of - // doing this cleanly is via boost, which we don't include. - - // Construct the filename from basepath & current frame number - std::ostringstream oss; - oss << m_path << "/" << - std::setw(m_frame_field_width) << std::setfill('0') << m_frame_number << ".png"; - - // Save the png - save(screen, oss.str()); - - m_frame_number++; -} - - diff --git a/atari_py/ale_interface/src/common/ScreenExporter.hpp b/atari_py/ale_interface/src/common/ScreenExporter.hpp deleted file mode 100644 index 91f0d0c..0000000 --- a/atari_py/ale_interface/src/common/ScreenExporter.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* ***************************************************************************** - * A.L.E (Arcade Learning Environment) - * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and - * the Reinforcement Learning and Artificial Intelligence Laboratory - * Released under the GNU General Public License; see License.txt for details. - * - * Based on: Stella -- "An Atari 2600 VCS Emulator" - * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team - * - * ***************************************************************************** - * ScreenExporter.hpp - * - * A class for exporting Atari 2600 frames as PNGs. - * - **************************************************************************** */ - -#ifndef __SCREEN_EXPORTER_HPP__ -#define __SCREEN_EXPORTER_HPP__ - -#include -#include "display_screen.h" -#include "../environment/ale_screen.hpp" - -class ScreenExporter { - - public: - - /** Creates a new ScreenExporter which can be used to save screens using save(filename). */ - ScreenExporter(ColourPalette &palette); - - /** Creates a new ScreenExporter which will save frames successively in the directory provided. - Frames are sequentially named with 6 digits, starting at 000000. */ - ScreenExporter(ColourPalette &palette, const std::string &path); - - /** Save the given screen to the given filename. No paths are created. */ - void save(const ALEScreen &screen, const std::string &filename) const; - - /** Save the given screen according to our own internal numbering. */ - void saveNext(const ALEScreen &screen); - - private: - - ColourPalette &m_palette; - - /** The next frame number. */ - int m_frame_number; - - /** The width of the frame number when constructing filenames (set to 6). */ - int m_frame_field_width; - - /** The directory where we save successive frames. */ - std::string m_path; -}; - -#endif // __SCREEN_EXPORTER_HPP__ - - - diff --git a/atari_py/ale_interface/src/common/SoundNull.hxx b/atari_py/ale_interface/src/common/SoundNull.hxx index ecb6d88..adc964b 100644 --- a/atari_py/ale_interface/src/common/SoundNull.hxx +++ b/atari_py/ale_interface/src/common/SoundNull.hxx @@ -41,7 +41,7 @@ class SoundNull : public Sound using the object. */ SoundNull(OSystem* osystem); - + /** Destructor */ @@ -54,7 +54,7 @@ class SoundNull : public Sound @param enable Either true or false, to enable or disable the sound system @return Whether the sound system was enabled or disabled */ - void setEnabled(bool enable) { } + void setEnabled(bool) { } /** The system cycle counter is being adjusting by the specified amount. Any @@ -62,14 +62,14 @@ class SoundNull : public Sound @param amount The amount the cycle counter is being adjusted by */ - void adjustCycleCounter(Int32 amount) { } + void adjustCycleCounter(Int32) { } /** Sets the number of channels (mono or stereo sound). @param channels The number of channels */ - void setChannels(uInt32 channels) { } + void setChannels(uInt32) { } /** Sets the display framerate. Sound generation for NTSC and PAL games @@ -77,7 +77,7 @@ class SoundNull : public Sound @param framerate The base framerate depending on NTSC or PAL ROM */ - void setFrameRate(uInt32 framerate) { } + void setFrameRate(uInt32) { } /** Initializes the sound device. This must be called before any @@ -103,7 +103,7 @@ class SoundNull : public Sound @param state Mutes sound if true, unmute if false */ - void mute(bool state) { } + void mute(bool) { } /** Reset the sound device. @@ -117,7 +117,7 @@ class SoundNull : public Sound @param value The value to save into the register @param cycle The system cycle at which the register is being updated */ - void set(uInt16 addr, uInt8 value, Int32 cycle) { } + void set(uInt16, uInt8, Int32) { } /** Sets the volume of the sound device to the specified level. The @@ -126,7 +126,7 @@ class SoundNull : public Sound @param percent The new volume percentage level for the sound device */ - void setVolume(Int32 percent) { } + void setVolume(Int32) { } /** Adjusts the volume of the sound device based on the given direction. @@ -134,7 +134,7 @@ class SoundNull : public Sound @param direction Increase or decrease the current volume by a predefined amount based on the direction (1 = increase, -1 =decrease) */ - void adjustVolume(Int8 direction) { } + void adjustVolume(Int8) { } /** * Tells the sound engine to record one frame's worth of sound. diff --git a/atari_py/ale_interface/src/common/display_screen.h b/atari_py/ale_interface/src/common/display_screen.h index ffe4abd..60910de 100644 --- a/atari_py/ale_interface/src/common/display_screen.h +++ b/atari_py/ale_interface/src/common/display_screen.h @@ -68,7 +68,7 @@ class DisplayScreen { /** A dummy class that simply ignores display events. */ class DisplayScreen { public: - DisplayScreen(MediaSource* mediaSource, Sound* sound, ColourPalette &palette) {} + DisplayScreen(MediaSource*, Sound*, ColourPalette &) {} void display_screen() {} bool manual_control_engaged() { return false; } Action getUserAction() { return UNDEFINED; } diff --git a/atari_py/ale_interface/src/common/misc_tools.h b/atari_py/ale_interface/src/common/misc_tools.h index fe1ddb8..8e34d5a 100644 --- a/atari_py/ale_interface/src/common/misc_tools.h +++ b/atari_py/ale_interface/src/common/misc_tools.h @@ -17,7 +17,7 @@ #include "Constants.h" -#if (defined(WIN32) || defined(__MINGW32__)) +#if (defined(_WIN32) || defined(__MINGW32__)) #include #endif @@ -54,7 +54,7 @@ inline void bound(int& x, int lower_bound, int upper_bound) { /* ***************************************************************************** Return time in milliseconds. **************************************************************************** */ -#if (defined(WIN32) || defined(__MINGW32__)) +#if (defined(_WIN32) || defined(__MINGW32__)) #include inline long timeMillis() { diff --git a/atari_py/ale_interface/src/common/module.mk b/atari_py/ale_interface/src/common/module.mk index 336f4fe..7e977a4 100644 --- a/atari_py/ale_interface/src/common/module.mk +++ b/atari_py/ale_interface/src/common/module.mk @@ -3,12 +3,11 @@ MODULE := src/common MODULE_OBJS := \ src/common/SoundNull.o \ src/common/SoundSDL.o \ - src/common/SoundExporter.o\ + src/common/SoundExporter.o \ src/common/display_screen.o \ src/common/ColourPalette.o \ src/common/ScreenExporter.o \ src/common/Constants.o \ - src/common/Defaults.o \ src/common/Log.o MODULE_DIRS += \ diff --git a/atari_py/ale_interface/src/common/mt19937.hpp b/atari_py/ale_interface/src/common/mt19937.hpp new file mode 100644 index 0000000..9ff4eda --- /dev/null +++ b/atari_py/ale_interface/src/common/mt19937.hpp @@ -0,0 +1,117 @@ +/* Copyright (C) 2019 by Nikita Kniazev + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef NK_MT19937 +#define NK_MT19937 + +#include + +#if defined(_MSC_VER) && (_MSC_VER < 1600) +typedef unsigned __int32 uint32_t; +#else +# include +# include +#endif + +class mt19937 +{ +public: + typedef uint32_t result_type; + + static const size_t word_size = 32; + static const size_t state_size = 624; + static const size_t shift_size = 397; + static const size_t mask_bits = 31; + static const uint32_t xor_mask = 0x9908b0df; + static const size_t tempering_u = 11; + static const uint32_t tempering_d = 0xffffffff; + static const size_t tempering_s = 7; + static const uint32_t tempering_b = 0x9d2c5680; + static const size_t tempering_t = 15; + static const uint32_t tempering_c = 0xefc60000; + static const size_t tempering_l = 18; + static const uint32_t initialization_multiplier = 1812433253; + static const uint32_t default_seed = 5489u; + + + explicit mt19937(uint32_t value = default_seed) { seed(value); } + + void seed(uint32_t value = default_seed) + { + state[0] = value; + for (uint32_t i = 1; i < state_size; ++i) + state[i] = i + initialization_multiplier * (state[i - 1] ^ (state[i - 1] >> 30)); + index = 0; + } + + result_type operator()() + { + uint32_t next = (index + 1) % state_size; + + uint32_t y = (0x80000000 & state[index]) | (0x7fffffff & state[next]); + uint32_t r = state[index] = state[(index + shift_size) % state_size] ^ (y >> 1) ^ ((-(y & 1)) & xor_mask); + r ^= r >> tempering_u & tempering_d; + r ^= r << tempering_s & tempering_b; + r ^= r << tempering_t & tempering_c; + r ^= r >> tempering_l; + + index = next; + + return r; + } + + result_type (min)() const { return 0; } + result_type (max)() const { return 0xffffffffUL; } + + template + friend OStream& operator<<(OStream& os, mt19937 const& g) + { + typename OStream::fmtflags saved_flags = os.flags(os.dec); + + for (uint32_t i = g.index; i < state_size; ++i) + os << g.state[i] << ' '; + for (uint32_t i = 0; i < g.index; ++i) + os << g.state[i] << ' '; + + os.flags(saved_flags); + + return os; + } + + template + friend IStream& operator>>(IStream& is, mt19937& g) + { + typename IStream::fmtflags saved_flags = is.flags(is.dec | is.skipws); + + uint32_t tmp[state_size]; + for (uint32_t i = 0; i < state_size; ++i) + is >> tmp[i]; + + is.flags(saved_flags); + + if (is.good()) { + for (uint32_t i = 0; i < state_size; ++i) + g.state[i] = tmp[i]; + g.index = 0; + } + + return is; + } + +private: + uint32_t state[state_size]; + uint32_t index; +}; + +#endif // NK_MT19937 diff --git a/atari_py/ale_interface/src/common/scoped_ptr.hpp b/atari_py/ale_interface/src/common/scoped_ptr.hpp new file mode 100644 index 0000000..6c3aabb --- /dev/null +++ b/atari_py/ale_interface/src/common/scoped_ptr.hpp @@ -0,0 +1,63 @@ +/* Copyright (C) 2019 by Nikita Kniazev + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef NK_SCOPED_PTR +#define NK_SCOPED_PTR + +namespace utils { + +struct bool_conversion { int padding; int truth; }; +typedef int bool_conversion::* explicit_bool; + +} + +template +class scoped_ptr { +public: + typedef T* pointer; + typedef T element_type; + + scoped_ptr() throw() : p_() {} + explicit scoped_ptr(pointer p) throw() : p_(p) {} + ~scoped_ptr() { destroy(); } + + element_type& operator*() const { return *get(); } + pointer operator->() const throw() { return get(); } + pointer get() const throw() { return p_; } + operator bool() const throw() { return get() != pointer(); } + operator utils::explicit_bool() const + { + return get() != pointer() ? &utils::bool_conversion::truth + : utils::explicit_bool(0); + } + + pointer release() throw() { pointer p = p_; p_ = pointer(); return p; } + void reset(pointer p = pointer()) throw() { destroy(); p_ = p; } + void swap(scoped_ptr& u) throw() + { + pointer p = release(); + p_ = u.release(); + u.p_ = p; + } + +private: + scoped_ptr(const scoped_ptr&); + scoped_ptr& operator=(const scoped_ptr&); + + void destroy() { delete get(); } + + pointer p_; +}; + +#endif // ALE_SCOPED_PTR diff --git a/atari_py/ale_interface/src/controllers/ale_controller.cpp b/atari_py/ale_interface/src/controllers/ale_controller.cpp index 17e70db..12ed061 100644 --- a/atari_py/ale_interface/src/controllers/ale_controller.cpp +++ b/atari_py/ale_interface/src/controllers/ale_controller.cpp @@ -24,7 +24,7 @@ ALEController::ALEController(OSystem* osystem): m_osystem(osystem), - m_settings(buildRomRLWrapper(m_osystem->settings().getString("rom_file"))), + m_settings(buildRomRLWrapper(m_osystem->settings().getString("rom_name"))), m_environment(m_osystem, m_settings.get()) { if (m_settings.get() == NULL) { diff --git a/atari_py/ale_interface/src/controllers/ale_controller.hpp b/atari_py/ale_interface/src/controllers/ale_controller.hpp index 8491741..7b3a439 100644 --- a/atari_py/ale_interface/src/controllers/ale_controller.hpp +++ b/atari_py/ale_interface/src/controllers/ale_controller.hpp @@ -21,6 +21,7 @@ #include "../emucore/OSystem.hxx" #include "../emucore/m6502/src/System.hxx" #include "../environment/stella_environment.hpp" +#include "../common/scoped_ptr.hpp" class ALEController { public: @@ -40,7 +41,7 @@ class ALEController { protected: OSystem* m_osystem; - std::auto_ptr m_settings; + scoped_ptr m_settings; StellaEnvironment m_environment; }; diff --git a/atari_py/ale_interface/src/controllers/fifo_controller.cpp b/atari_py/ale_interface/src/controllers/fifo_controller.cpp index bca59c4..bf45984 100644 --- a/atari_py/ale_interface/src/controllers/fifo_controller.cpp +++ b/atari_py/ale_interface/src/controllers/fifo_controller.cpp @@ -89,7 +89,7 @@ void FIFOController::handshake() { // send the width and height of the screen through the pipe char out_buffer [1024]; - snprintf (out_buffer, sizeof(out_buffer), "%d-%d\n", + BSPF_snprintf(out_buffer, sizeof(out_buffer), "%d-%d\n", (int)m_environment.getScreen().width(), (int)m_environment.getScreen().height()); @@ -98,7 +98,10 @@ void FIFOController::handshake() { // Read in agent's response char in_buffer [1024]; - fgets (in_buffer, sizeof(in_buffer), m_fin); + if (fgets (in_buffer, sizeof(in_buffer), m_fin) == NULL) { + // If The agent hung up, stop the handshake. + return; + } // Parse response: send_screen, send_ram, , send_RL char * token = strtok (in_buffer,",\n"); @@ -232,8 +235,14 @@ void FIFOController::sendRL() { void FIFOController::readAction(Action& action_a, Action& action_b) { // Read the new action from the pipe, as a comma-separated pair char in_buffer[2048]; - fgets (in_buffer, sizeof(in_buffer), m_fin); - + if (fgets (in_buffer, sizeof(in_buffer), m_fin) == NULL) { + // If fgets returns NULL, we set the two actions to NOOP here. This is probably because the + // other side has hung up. We'll let the process terminate as usual. + action_a = PLAYER_A_NOOP; + action_b = PLAYER_B_NOOP; + return; + } + char * token = strtok (in_buffer,",\n"); action_a = (Action)atoi(token); diff --git a/atari_py/ale_interface/src/emucore/Cart3E.cxx b/atari_py/ale_interface/src/emucore/Cart3E.cxx index a2fbf1a..8d2e5fe 100644 --- a/atari_py/ale_interface/src/emucore/Cart3E.cxx +++ b/atari_py/ale_interface/src/emucore/Cart3E.cxx @@ -40,7 +40,7 @@ Cartridge3E::Cartridge3E(const uInt8* image, uInt32 size) } // Initialize RAM with random values - class Random random; + class Random& random = Random::getInstance(); for(uInt32 i = 0; i < 32768; ++i) { myRam[i] = random.next(); diff --git a/atari_py/ale_interface/src/emucore/CartAR.cxx b/atari_py/ale_interface/src/emucore/CartAR.cxx index f446ffe..6a93cf7 100644 --- a/atari_py/ale_interface/src/emucore/CartAR.cxx +++ b/atari_py/ale_interface/src/emucore/CartAR.cxx @@ -40,7 +40,8 @@ CartridgeAR::CartridgeAR(const uInt8* image, uInt32 size, bool fastbios) memcpy(myLoadImages, image, size); // Initialize RAM with random values - class Random random; + class Random& random = Random::getInstance(); + for(i = 0; i < 6 * 1024; ++i) { myImage[i] = random.next(); diff --git a/atari_py/ale_interface/src/emucore/CartCV.cxx b/atari_py/ale_interface/src/emucore/CartCV.cxx index 84d6dae..51c2120 100644 --- a/atari_py/ale_interface/src/emucore/CartCV.cxx +++ b/atari_py/ale_interface/src/emucore/CartCV.cxx @@ -38,7 +38,8 @@ CartridgeCV::CartridgeCV(const uInt8* image, uInt32 size) } // Initialize RAM with random values - class Random random; + class Random& random = Random::getInstance(); + for(uInt32 i = 0; i < 1024; ++i) { myRAM[i] = random.next(); diff --git a/atari_py/ale_interface/src/emucore/CartE7.cxx b/atari_py/ale_interface/src/emucore/CartE7.cxx index 6d279bb..4f6cdbf 100644 --- a/atari_py/ale_interface/src/emucore/CartE7.cxx +++ b/atari_py/ale_interface/src/emucore/CartE7.cxx @@ -35,7 +35,8 @@ CartridgeE7::CartridgeE7(const uInt8* image) } // Initialize RAM with random values - class Random random; + class Random& random = Random::getInstance(); + for(uInt32 i = 0; i < 2048; ++i) { myRAM[i] = random.next(); diff --git a/atari_py/ale_interface/src/emucore/CartF4SC.cxx b/atari_py/ale_interface/src/emucore/CartF4SC.cxx index ceb4f94..8d26248 100644 --- a/atari_py/ale_interface/src/emucore/CartF4SC.cxx +++ b/atari_py/ale_interface/src/emucore/CartF4SC.cxx @@ -35,7 +35,8 @@ CartridgeF4SC::CartridgeF4SC(const uInt8* image) } // Initialize RAM with random values - class Random random; + class Random& random = Random::getInstance(); + for(uInt32 i = 0; i < 128; ++i) { myRAM[i] = random.next(); diff --git a/atari_py/ale_interface/src/emucore/CartF6SC.cxx b/atari_py/ale_interface/src/emucore/CartF6SC.cxx index 9b156b4..cbfff3b 100644 --- a/atari_py/ale_interface/src/emucore/CartF6SC.cxx +++ b/atari_py/ale_interface/src/emucore/CartF6SC.cxx @@ -35,7 +35,8 @@ CartridgeF6SC::CartridgeF6SC(const uInt8* image) } // Initialize RAM with random values - class Random random; + class Random& random = Random::getInstance(); + for(uInt32 i = 0; i < 128; ++i) { myRAM[i] = random.next(); diff --git a/atari_py/ale_interface/src/emucore/CartF8SC.cxx b/atari_py/ale_interface/src/emucore/CartF8SC.cxx index 57830e0..27d913e 100644 --- a/atari_py/ale_interface/src/emucore/CartF8SC.cxx +++ b/atari_py/ale_interface/src/emucore/CartF8SC.cxx @@ -35,7 +35,8 @@ CartridgeF8SC::CartridgeF8SC(const uInt8* image) } // Initialize RAM with random values - class Random random; + class Random& random = Random::getInstance(); + for(uInt32 i = 0; i < 128; ++i) { myRAM[i] = random.next(); diff --git a/atari_py/ale_interface/src/emucore/CartFASC.cxx b/atari_py/ale_interface/src/emucore/CartFASC.cxx index c8c596c..b873a47 100644 --- a/atari_py/ale_interface/src/emucore/CartFASC.cxx +++ b/atari_py/ale_interface/src/emucore/CartFASC.cxx @@ -35,7 +35,8 @@ CartridgeFASC::CartridgeFASC(const uInt8* image) } // Initialize RAM with random values - class Random random; + class Random& random = Random::getInstance(); + for(uInt32 i = 0; i < 256; ++i) { myRAM[i] = random.next(); diff --git a/atari_py/ale_interface/src/emucore/CartMC.cxx b/atari_py/ale_interface/src/emucore/CartMC.cxx index a21fd8b..63f71c7 100644 --- a/atari_py/ale_interface/src/emucore/CartMC.cxx +++ b/atari_py/ale_interface/src/emucore/CartMC.cxx @@ -38,7 +38,8 @@ CartridgeMC::CartridgeMC(const uInt8* image, uInt32 size) myRAM = new uInt8[32 * 1024]; // Initialize RAM with random values - class Random random; + class Random& random = Random::getInstance(); + for(i = 0; i < 32 * 1024; ++i) { myRAM[i] = random.next(); diff --git a/atari_py/ale_interface/src/emucore/Console.cxx b/atari_py/ale_interface/src/emucore/Console.cxx index 27233a8..57ebf00 100644 --- a/atari_py/ale_interface/src/emucore/Console.cxx +++ b/atari_py/ale_interface/src/emucore/Console.cxx @@ -72,12 +72,6 @@ Console::Console(OSystem* osystem, Cartridge* cart, const Properties& props) mySystem = 0; myEvent = 0; - std::string seedStr = myOSystem->settings().getString("random_seed").c_str(); - if (seedStr == "time") - Random::seed((uInt32)time(NULL)); - else - Random::seed((uInt32)atoi(seedStr.c_str())); - // Attach the event subsystem to the current console //ALE myEvent = myOSystem->eventHandler().event(); myEvent = myOSystem->event(); @@ -154,7 +148,7 @@ Console::Console(OSystem* osystem, Cartridge* cart, const Properties& props) mySwitches = new Switches(*myEvent, myProperties); // Now, we can construct the system and components - mySystem = new System(13, 6); + mySystem = new System(); // Inform the controllers about the system myControllers[0]->setSystem(mySystem); @@ -397,8 +391,8 @@ void Console::initializeAudio() */ void Console::fry() const { - for (int ZPmem=0; ZPmem<0x100; ZPmem += randNumGen.next() % 4) - mySystem->poke(ZPmem, mySystem->peek(ZPmem) & (uInt8)randNumGen.next() % 256); + for (int ZPmem=0; ZPmem<0x100; ZPmem += myOSystem->rng().next() % 4) + mySystem->poke(ZPmem, mySystem->peek(ZPmem) & (uInt8)myOSystem->rng().next() % 256); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/atari_py/ale_interface/src/emucore/Console.hxx b/atari_py/ale_interface/src/emucore/Console.hxx index 23cd046..ef2f61b 100644 --- a/atari_py/ale_interface/src/emucore/Console.hxx +++ b/atari_py/ale_interface/src/emucore/Console.hxx @@ -104,6 +104,13 @@ class Console */ System& system() const { return *mySystem; } + /** + Returns the OSystem for this emulator. + + @return The OSystem. + */ + OSystem& osystem() const { return *myOSystem; } + /** Get the cartridge used by the console which contains the ROM code @@ -247,9 +254,6 @@ class Console // A RIOT of my own! (...with apologies to The Clash...) M6532 *myRiot; - //Random number generator - mutable Random randNumGen; - #ifdef ATARIVOX_SUPPORT AtariVox *vox; #endif diff --git a/atari_py/ale_interface/src/emucore/Control.cxx b/atari_py/ale_interface/src/emucore/Control.cxx index 61984ad..314f743 100644 --- a/atari_py/ale_interface/src/emucore/Control.cxx +++ b/atari_py/ale_interface/src/emucore/Control.cxx @@ -26,14 +26,14 @@ Controller::Controller(Jack jack, const Event& event, Type type) myType(type) { } - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Controller::~Controller() { } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const Controller::Type Controller::type() +Controller::Type Controller::type() { return myType; } diff --git a/atari_py/ale_interface/src/emucore/Control.hxx b/atari_py/ale_interface/src/emucore/Control.hxx index 1b3a542..07a0bb2 100644 --- a/atari_py/ale_interface/src/emucore/Control.hxx +++ b/atari_py/ale_interface/src/emucore/Control.hxx @@ -87,7 +87,7 @@ class Controller @param type The type for this controller */ Controller(Jack jack, const Event& event, Type type); - + /** Destructor */ @@ -96,7 +96,7 @@ class Controller /** Returns the type of this controller. */ - const Type type(); + Type type(); /** Inform this controller about the current System. diff --git a/atari_py/ale_interface/src/emucore/FSNode.hxx b/atari_py/ale_interface/src/emucore/FSNode.hxx index 4085651..3a59c77 100644 --- a/atari_py/ale_interface/src/emucore/FSNode.hxx +++ b/atari_py/ale_interface/src/emucore/FSNode.hxx @@ -1,8 +1,8 @@ //============================================================================ // -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa // SSSS tt ee ee ll ll aa // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" // SS SS tt ee ll ll aa aa @@ -47,8 +47,8 @@ * And if we ever want to support devices with no FS in the classical sense (Palm...), * we can build upon this. */ - -/* + +/* * TODO - Instead of starting with getRoot(), we should rather add a getDefaultDir() * call that on Unix might return the current dir or the users home dir... * i.e. the root dir is usually not the best starting point for browsing. @@ -120,8 +120,8 @@ class AbstractFilesystemNode { std::string first = displayName(); std::string second = node.displayName(); - transform(first.begin(), first.end(), first.begin(), (int(*)(int)) tolower); - transform(second.begin(), second.end(), second.begin(), (int(*)(int)) tolower); + std::transform(first.begin(), first.end(), first.begin(), (int(*)(int)) tolower); + std::transform(second.begin(), second.end(), second.begin(), (int(*)(int)) tolower); return first < second; } @@ -155,7 +155,7 @@ class AbstractFilesystemNode virtual AbstractFilesystemNode *parent() const = 0; /** - * This method is a rather ugly hack which is used internally by the + * This method is a rather ugly hack which is used internally by the * actual node implementions to wrap up raw nodes inside FilesystemNode * objects. We probably want to get rid of this eventually and replace it * with a cleaner / more elegant solution, but for now it works. diff --git a/atari_py/ale_interface/src/emucore/M6532.cxx b/atari_py/ale_interface/src/emucore/M6532.cxx index 4be0dcc..6a0c60a 100644 --- a/atari_py/ale_interface/src/emucore/M6532.cxx +++ b/atari_py/ale_interface/src/emucore/M6532.cxx @@ -23,6 +23,7 @@ #include "System.hxx" #include "Serializer.hxx" #include "Deserializer.hxx" +#include "OSystem.hxx" #include using namespace std; #include "../common/Log.hpp" @@ -31,13 +32,11 @@ using namespace std; M6532::M6532(const Console& console) : myConsole(console) { - class Random random; - // Randomize the 128 bytes of memory for(uInt32 t = 0; t < 128; ++t) { - myRAM[t] = random.next(); + myRAM[t] = myConsole.osystem().rng().next(); } // Initialize other data members @@ -58,9 +57,7 @@ const char* M6532::name() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void M6532::reset() { - class Random random; - - myTimer = 25 + (random.next() % 75); + myTimer = 25 + (myConsole.osystem().rng().next() % 75); myIntervalShift = 6; myCyclesWhenTimerSet = 0; myCyclesWhenInterruptReset = 0; diff --git a/atari_py/ale_interface/src/emucore/OSystem.cxx b/atari_py/ale_interface/src/emucore/OSystem.cxx index dc41027..69c770d 100644 --- a/atari_py/ale_interface/src/emucore/OSystem.cxx +++ b/atari_py/ale_interface/src/emucore/OSystem.cxx @@ -20,12 +20,10 @@ #include #include #include -#include +//Kojoley #include #include using namespace std; -//ALE #include "MediaFactory.hxx" - #ifdef DEBUGGER_SUPPORT #include "Debugger.hxx" #endif @@ -35,21 +33,13 @@ using namespace std; #endif #include "FSNode.hxx" -#include "unzip.h" #include "MD5.hxx" #include "Settings.hxx" #include "PropsSet.hxx" -//ALE #include "EventHandler.hxx" -#include "Event.hxx" //ALE -//ALE #include "Menu.hxx" -//ALE #include "CommandMenu.hxx" -//ALE #include "Launcher.hxx" -//ALE #include "Font.hxx" -//ALE #include "StellaFont.hxx" -//ALE #include "ConsoleFont.hxx" +#include "Event.hxx" #include "OSystem.hxx" #include "SoundSDL.hxx" -//ALE #include "Widget.hxx" + #define MAX_ROM_SIZE 512 * 1024 #include @@ -60,14 +50,14 @@ using namespace std; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSystem::OSystem() : - myEvent(NULL), //ALE + myEvent(NULL), mySound(NULL), mySettings(NULL), myPropSet(NULL), myConsole(NULL), myQuitLoop(false), mySkipEmulation(false), - myRomFile(""), + //Kojoley myRomFile(""), myFeatures(""), p_display_screen(NULL) { @@ -86,24 +76,16 @@ OSystem::OSystem() #ifdef CHEATCODE_SUPPORT myFeatures += "Cheats"; #endif - } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSystem::~OSystem() { - //ALE delete myMenu; - //ALE delete myCommandMenu; - //ALE delete myLauncher; - //ALE delete myFont; - //ALE delete myConsoleFont; - // Remove any game console that is currently attached deleteConsole(); // OSystem takes responsibility for framebuffer and sound, // since it created them - //ALE delete myFrameBuffer; if (mySound != NULL) delete mySound; @@ -121,14 +103,11 @@ OSystem::~OSystem() if (myPropSet != NULL) delete myPropSet; - //ALE delete myEventHandler; if (myEvent != NULL) delete myEvent; if (p_display_screen != NULL) { delete p_display_screen; } - - //ALE delete aiBase; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -174,11 +153,36 @@ bool OSystem::create() // that only have a single sound device (no hardware mixing) createSound(); - //ALE aiBase = new AIBase(this); + // Seed RNG. This will likely get re-called, e.g. by the ALEInterface, but is needed + // by other interfaces. + resetRNGSeed(); return true; } +void OSystem::resetRNGSeed() { + + // We seed the random number generator. The 'time' seed is somewhat redundant, since the + // rng defaults to time. But we'll do it anyway. + if (mySettings->getInt("random_seed") == 0) { + myRandGen.seed((uInt32)time(NULL)); + } else { + int seed = mySettings->getInt("random_seed"); + assert(seed >= 0); + myRandGen.seed((uInt32)seed); + } +} + +bool OSystem::saveState(Serializer& out) { + + // Here we serialize the RNG state. + return myRandGen.saveState(out); +} + +bool OSystem::loadState(Deserializer& in) { + return myRandGen.loadState(in); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void OSystem::setConfigPaths() { @@ -344,7 +348,7 @@ void OSystem::createSound() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool OSystem::createConsole(const string& romfile) +bool OSystem::createConsole(const string& rom, const string& name) { // Do a little error checking; it shouldn't be necessary if(myConsole) deleteConsole(); @@ -352,27 +356,20 @@ bool OSystem::createConsole(const string& romfile) bool retval = false; // If a blank ROM has been given, we reload the current one (assuming one exists) - if(romfile == "") + if(rom.empty()) { - if(myRomFile == "") - { - ale::Logger::Error << "ERROR: Rom file not specified ..." << endl; - return false; - } + ale::Logger::Error << "ERROR: Rom not specified ..." << endl; + return false; } - else - myRomFile = romfile; // Open the cartridge image and read it in - uInt8* image; - int size = -1; string md5; - if(openROM(myRomFile, md5, &image, &size)) + if(openROM(rom, name, md5)) { // Get all required info for creating a valid console Cartridge* cart = (Cartridge*) NULL; Properties props; - if(queryConsoleInfo(image, size, md5, &cart, props)) + if(queryConsoleInfo((const uInt8 *)rom.data(), rom.size(), md5, &cart, props)) { // Create an instance of the 2600 game console myConsole = new Console(this, cart, props); @@ -391,11 +388,9 @@ bool OSystem::createConsole(const string& romfile) if(mySettings->getBool("showinfo")) cerr << "Game console created:" << endl - << " ROM file: " << myRomFile << endl << myConsole->about() << endl; else ale::Logger::Info << "Game console created:" << endl - << " ROM file: " << myRomFile << endl << myConsole->about() << endl; // Update the timing info for a new console run @@ -406,20 +401,16 @@ bool OSystem::createConsole(const string& romfile) } else { - ale::Logger::Error << "ERROR: Couldn't create console for " << myRomFile << " ..." << endl; + ale::Logger::Error << "ERROR: Couldn't create console for rom ..." << endl; retval = false; } } else { - ale::Logger::Error << "ERROR: Couldn't open " << myRomFile << " ..." << endl; + ale::Logger::Error << "ERROR: Couldn't open rom ..." << endl; retval = false; } - // Free the image since we don't need it any longer - if(size != -1) { - delete[] image; - } if (mySettings->getBool("display_screen", true)) { #ifndef __USE_SDL ale::Logger::Error << "Screen display requires directive __USE_SDL to be defined." @@ -479,93 +470,32 @@ void OSystem::createLauncher() ALE */ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool OSystem::openROM(const string& rom, string& md5, uInt8** image, int* size) +bool OSystem::openROM(const string& rom, const string& name, string& md5) { - // Try to open the file as a zipped archive - // If that fails, we assume it's just a gzipped or normal data file - unzFile tz; - if((tz = unzOpen(rom.c_str())) != NULL) - { - if(unzGoToFirstFile(tz) == UNZ_OK) - { - unz_file_info ufo; - - for(;;) // Loop through all files for valid 2600 images - { - // Longer filenames might be possible, but I don't - // think people would name files that long in zip files... - char filename[1024]; - - unzGetCurrentFileInfo(tz, &ufo, filename, 1024, 0, 0, 0, 0); - filename[1023] = '\0'; - - if(strlen(filename) >= 4) - { - // Grab 3-character extension - char* ext = filename + strlen(filename) - 4; - - if(!BSPF_strcasecmp(ext, ".bin") || !BSPF_strcasecmp(ext, ".a26")) - break; - } - - // Scan the next file in the zip - if(unzGoToNextFile(tz) != UNZ_OK) - break; - } - - // Now see if we got a valid image - if(ufo.uncompressed_size <= 0) - { - unzClose(tz); - return false; - } - *size = ufo.uncompressed_size; - *image = new uInt8[*size]; - - // We don't have to check for any return errors from these functions, - // since if there are, 'image' will not contain a valid ROM and the - // calling method can take of it - unzOpenCurrentFile(tz); - unzReadCurrentFile(tz, *image, *size); - unzCloseCurrentFile(tz); - unzClose(tz); - } - else - { - unzClose(tz); - return false; - } - } - else - { - // Assume the file is either gzip'ed or not compressed at all - gzFile f = gzopen(rom.c_str(), "rb"); - if(!f) - return false; - - *image = new uInt8[MAX_ROM_SIZE]; - *size = gzread(f, *image, MAX_ROM_SIZE); - gzclose(f); - } + /* Kojoley + // Assume the file is either gzip'ed or not compressed at all + gzFile f = gzopen(rom.c_str(), "rb"); + if(!f) + return false; + + *image = new uInt8[MAX_ROM_SIZE]; + *size = gzread(f, *image, MAX_ROM_SIZE); + gzclose(f); + Kojoley */ // If we get to this point, we know we have a valid file to open // Now we make sure that the file has a valid properties entry - md5 = MD5(*image, *size); - - // Some games may not have a name, since there may not - // be an entry in stella.pro. In that case, we use the rom name - // and reinsert the properties object - Properties props; - myPropSet->getMD5(md5, props); + md5 = MD5((const uInt8 *)rom.data(), rom.size()); - string name = props.get(Cartridge_Name); - if(name == "Untitled") - { - // Get the filename from the rom pathname - string::size_type pos = rom.find_last_of(BSPF_PATH_SEPARATOR); - if(pos+1 != string::npos) + if (!name.empty()) { + // Some games may not have a name, since there may not + // be an entry in stella.pro. In that case, we use the rom name + // and reinsert the properties object + Properties props; + myPropSet->getMD5(md5, props); + + if(props.get(Cartridge_Name) == "Untitled") { - name = rom.substr(pos+1); props.set(Cartridge_MD5, md5); props.set(Cartridge_Name, name); myPropSet->insert(props, false); @@ -576,35 +506,30 @@ bool OSystem::openROM(const string& rom, string& md5, uInt8** image, int* size) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string OSystem::getROMInfo(const string& romfile) +string OSystem::getROMInfo(const string& rom, const string& name) { ostringstream buf; // Open the cartridge image and read it in - uInt8* image; - int size = -1; string md5; - if(openROM(romfile, md5, &image, &size)) + if(openROM(rom, name, md5)) { // Get all required info for creating a temporary console Cartridge* cart = (Cartridge*) NULL; Properties props; - if(queryConsoleInfo(image, size, md5, &cart, props)) + if(queryConsoleInfo((const uInt8 *)rom.data(), rom.size(), md5, &cart, props)) { Console* console = new Console(this, cart, props); if(console) buf << console->about(); else - buf << "ERROR: Couldn't get ROM info for " << romfile << " ..." << endl; + buf << "ERROR: Couldn't get ROM info for rom ..." << endl; delete console; } else - buf << "ERROR: Couldn't open " << romfile << " ..." << endl; + buf << "ERROR: Couldn't open rom ..." << endl; } - // Free the image and console since we don't need it any longer - if(size != -1) - delete[] image; return buf.str(); } diff --git a/atari_py/ale_interface/src/emucore/OSystem.hxx b/atari_py/ale_interface/src/emucore/OSystem.hxx index 1cec3d1..5247e4d 100644 --- a/atari_py/ale_interface/src/emucore/OSystem.hxx +++ b/atari_py/ale_interface/src/emucore/OSystem.hxx @@ -39,7 +39,7 @@ class VideoDialog; #include "m6502/src/bspf/src/bspf.hxx" #include "../common/display_screen.h" #include "../common/ColourPalette.hpp" -#include "../common/ScreenExporter.hpp" +//Kojoley #include "../common/ScreenExporter.hpp" #include "../common/Log.hpp" struct Resolution { @@ -223,8 +223,8 @@ class OSystem /** Get the maximum dimensions of a window for the video hardware. */ - const uInt32 desktopWidth() const { return myDesktopWidth; } - const uInt32 desktopHeight() const { return myDesktopHeight; } + uInt32 desktopWidth() const { return myDesktopWidth; } + uInt32 desktopHeight() const { return myDesktopHeight; } /** Get the supported fullscreen resolutions for the video hardware. @@ -282,7 +282,7 @@ class OSystem @return String representing the full path of the ROM file. */ - const std::string& romFile() const { return myRomFile; } + //Kojoley const std::string& romFile() const { return myRomFile; } /** Switches between software and OpenGL framebuffer modes. @@ -293,9 +293,11 @@ class OSystem Creates a new game console from the specified romfile. @param romfile The full pathname of the ROM to use + @param name The ROM name @return True on successful creation, otherwise false */ - bool createConsole(const std::string& romfile = ""); + bool createConsole(const std::string& romfile = "", + const std::string& name = ""); /** Deletes the currently defined console, if it exists. @@ -313,9 +315,11 @@ class OSystem Console object and querying it. @param romfile The full pathname of the ROM to use + @param name The ROM name @return Some information about this ROM */ - std::string getROMInfo(const std::string& romfile); + std::string getROMInfo(const std::string& romfile, + const std::string& name = ""); /** The features which are conditionally compiled into Stella. @@ -328,13 +332,13 @@ class OSystem Open the given ROM and return an array containing its contents. @param rom The absolute pathname of the ROM file + @param name The ROM name @param md5 The md5 calculated from the ROM file - @param image A pointer to store the ROM data - Note, the calling method is responsible for deleting this - @param size The amount of data read into the image array @return False on any errors, else true */ - bool openROM(const std::string& rom, std::string& md5, uInt8** image, int* size); + bool openROM(const std::string& rom, + const std::string& name, + std::string& md5); /** Issue a quit event to the OSystem. @@ -343,6 +347,26 @@ class OSystem void skipEmulation() { mySkipEmulation = true; } + /** + Returns the random number generator for this emulator. + */ + Random& rng() { return myRandGen; } + + /** + Resets the seed for our random number generator. + */ + void resetRNGSeed(); + + /** + Serializes the OSystem state. + */ + bool saveState(Serializer& out); + + /** + Deserializes the OSystem state. + */ + bool loadState(Deserializer& in); + public: ////////////////////////////////////////////////////////////////////// // The following methods are system-specific and must be implemented @@ -390,7 +414,7 @@ class OSystem */ //ALE virtual void stateChanged(EventHandler::State state); - + protected: /** Query the OSystem video hardware for resolution information. @@ -414,7 +438,7 @@ class OSystem void setConfigFile(const std::string& file) { myConfigFile = file; } - + protected: // Pointer to the EventHandler object //ALE EventHandler* myEventHandler; @@ -435,9 +459,10 @@ class OSystem // Pointer to the (currently defined) Console object Console* myConsole; - - + // Random number generator shared across the emulator's components + Random myRandGen; + // Pointer to the Menu object //ALE Menu* myMenu; @@ -455,7 +480,7 @@ class OSystem // Pointer to the AI object //ALE AIBase *aiBase; - + // Maximum dimensions of the desktop area uInt32 myDesktopWidth, myDesktopHeight; @@ -482,7 +507,7 @@ class OSystem std::string myPropertiesFile; std::string myGameListCacheFile; - std::string myRomFile; + //Kojoley std::string myRomFile; std::string myFeatures; @@ -494,7 +519,7 @@ class OSystem // The font object to use for the console/debugger //ALE GUI::Font* myConsoleFont; - + public: //ALE // Time per frame for a video update, based on the current framerate uInt32 myTimePerFrame; @@ -515,7 +540,7 @@ class OSystem //ALE static uInt32 ourGUIColors[kNumUIPalettes][kNumColors-256]; public: DisplayScreen* p_display_screen; //MHAUSKN - + private: ColourPalette m_colour_palette; diff --git a/atari_py/ale_interface/src/emucore/Random.cxx b/atari_py/ale_interface/src/emucore/Random.cxx index 2d4e74a..42d7c62 100644 --- a/atari_py/ale_interface/src/emucore/Random.cxx +++ b/atari_py/ale_interface/src/emucore/Random.cxx @@ -18,59 +18,115 @@ #include #include "Random.hxx" +#include "Serializer.hxx" +#include "Deserializer.hxx" -// TODO(mgb): bring this include in once we switch to C++11. -// #include -#include "TinyMT/tinymt32.h" +#include "../common/mt19937.hpp" +#include -// The random number generator is defined here to avoid having to expose tinymt32.h. -namespace RandomStatic { +// A static Random object for compatibility purposes. Don't use this. +Random Random::s_random; - typedef tinymt32_t randgen_t; - // Random number generator - randgen_t rndGenerator; +// Implementation of Random's random number generator wrapper. +class Random::Impl { + + typedef mt19937 randgen_t; + + public: + + Impl(); + + // Implementations of the methods defined in Random.hpp. + void seed(uInt32 value); + uInt32 next(); + double nextDouble(); + + private: + + friend class Random; + + // Seed to use for creating new random number generators + uInt32 m_seed; + + // Random number generator + randgen_t m_randgen; +}; + +Random::Impl::Impl() +{ + // Initialize seed to time } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Random::Impl::seed(uInt32 value) +{ + m_seed = value; + m_randgen.seed(m_seed); +} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Random::seed(uInt32 value) +uInt32 Random::Impl::next() { - ourSeed = value; - ourSeeded = true; - // TODO(mgb): this is the C++11 variant. - // rndGenerator.seed(ourSeed); + return m_randgen(); +} - tinymt32_init(&RandomStatic::rndGenerator, ourSeed); +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +double Random::Impl::nextDouble() +{ + return m_randgen() / double(m_randgen.max() + 1.0); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Random::Random() +Random::Random() : + m_pimpl(new Random::Impl()) { - // If we haven't been seeded then seed ourself - if(!ourSeeded) - seed((uInt32) time(NULL)); } - + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Random::~Random() { + if (m_pimpl != NULL) { + delete m_pimpl; + m_pimpl = NULL; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Random::seed(uInt32 value) +{ + m_pimpl->seed(value); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 Random::next() { - // TODO(mgb): C++11 - // return rndGenerator(); - return static_cast(tinymt32_generate_uint32(&RandomStatic::rndGenerator)); + return m_pimpl->next(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - double Random::nextDouble() { - // TODO(mgb): C++11 - // return rndGenerator() / double(rndGenerator.max() + 1.0); - return tinymt32_generate_32double(&RandomStatic::rndGenerator); + return m_pimpl->nextDouble(); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Random::ourSeed = 0; +Random& Random::getInstance() { + return s_random; +} -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Random::ourSeeded = false; +bool Random::saveState(Serializer& ser) { + // The mt19937 object's serialization of choice is into a string. + std::ostringstream oss; + oss << m_pimpl->m_randgen; + ser.putString(oss.str()); + + return true; +} +bool Random::loadState(Deserializer& deser) { + // Deserialize into a string. + std::istringstream iss(deser.getString()); + + iss >> m_pimpl->m_randgen; + + return true; +} diff --git a/atari_py/ale_interface/src/emucore/Random.hxx b/atari_py/ale_interface/src/emucore/Random.hxx index 1ed0518..4aef44e 100644 --- a/atari_py/ale_interface/src/emucore/Random.hxx +++ b/atari_py/ale_interface/src/emucore/Random.hxx @@ -21,13 +21,12 @@ #include "m6502/src/bspf/src/bspf.hxx" -/** - This is a quick-and-dirty random number generator. It is based on - information in Chapter 7 of "Numerical Recipes in C". It's a simple - linear congruential generator. +class Serializer; +class Deserializer; - @author Bradford W. Mott - @version $Id: Random.hxx,v 1.4 2007/01/01 18:04:49 stephena Exp $ +/** + This Random class uses a Mersenne Twister to provide pseudorandom numbers. + The class itself is derived from the original 'Random' class by Bradford W. Mott. */ class Random { @@ -39,13 +38,15 @@ class Random @param value The value to seed the random number generator with */ - static void seed(uInt32 value); + void seed(uInt32 value); /** Create a new random number generator */ Random(); - + + ~Random(); + /** Answer the next random number from the random number generator @@ -60,13 +61,28 @@ class Random */ double nextDouble(); - private: + // Returns a static Random object. DO NOT USE THIS. This is mostly meant for use by the + // code for the various cartridges. + static Random& getInstance(); + + /** + Serializes the RNG state. + */ + bool saveState(Serializer& out); + + /** + Deserializes the RNG state. + */ + bool loadState(Deserializer& in); + + private: - // Seed to use for creating new random number generators - static uInt32 ourSeed; + // Actual rng (implementation hidden away from the header to avoid depending on rng libraries). + class Impl; + Impl *m_pimpl; - // Indicates if seed has been set or not - static bool ourSeeded; + // A static Random object. Don't use this. + static Random s_random; }; #endif diff --git a/atari_py/ale_interface/src/emucore/Settings.cxx b/atari_py/ale_interface/src/emucore/Settings.cxx index c8ccb73..b5c14e8 100644 --- a/atari_py/ale_interface/src/emucore/Settings.cxx +++ b/atari_py/ale_interface/src/emucore/Settings.cxx @@ -93,6 +93,8 @@ Settings::Settings(OSystem* osystem) : myOSystem(osystem) { setInternal("uipalette", "0"); setInternal("mwheel", "4"); setInternal("autoslot", "false"); + + setDefaultSettings(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -108,8 +110,8 @@ void Settings::loadConfig(const char* config_file){ ifstream in(config_file); if(!in || !in.is_open()) { - // ale::Logger::Warning << "Warning: couldn't load settings file: " << config_file << "\n"; - return; + ale::Logger::Warning << "Warning: couldn't load settings file: " << config_file << std::endl; + return; } while(getline(in, line)) { @@ -379,46 +381,58 @@ void Settings::saveConfig() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Settings::setInt(const string& key, const int value) { - ostringstream stream; + std::ostringstream stream; stream << value; - if(int idx = getInternalPos(key) != -1) + if(int idx = getInternalPos(key) != -1){ setInternal(key, stream.str(), idx); - else + } + else{ + verifyVariableExistence(intSettings, key); setExternal(key, stream.str()); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Settings::setFloat(const string& key, const float value) { - ostringstream stream; + std::ostringstream stream; stream << value; - if(int idx = getInternalPos(key) != -1) + if(int idx = getInternalPos(key) != -1){ setInternal(key, stream.str(), idx); - else + } + else{ + verifyVariableExistence(floatSettings, key); setExternal(key, stream.str()); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Settings::setBool(const string& key, const bool value) { - ostringstream stream; + std::ostringstream stream; stream << value; - if(int idx = getInternalPos(key) != -1) + if(int idx = getInternalPos(key) != -1){ setInternal(key, stream.str(), idx); - else + } + else{ + verifyVariableExistence(boolSettings, key); setExternal(key, stream.str()); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Settings::setString(const string& key, const string& value) { - if(int idx = getInternalPos(key) != -1) + if(int idx = getInternalPos(key) != -1){ setInternal(key, value, idx); - else + } + else{ + verifyVariableExistence(stringSettings, key); setExternal(key, value); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -528,7 +542,7 @@ const string& Settings::getString(const string& key, bool strict) const { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Settings::setSize(const string& key, const int value1, const int value2) { - ostringstream buf; + std::ostringstream buf; buf << value1 << "x" << value2; setString(key, buf.str()); } @@ -673,3 +687,58 @@ Settings& Settings::operator = (const Settings&) return *this; } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Settings::setDefaultSettings() { + + // Stella settings + stringSettings.insert(pair("cpu", "low")); // Reduce CPU emulation fidelity for speed + + // Controller settings + intSettings.insert(pair("max_num_frames", 0)); + intSettings.insert(pair("max_num_frames_per_episode", 0)); + + // FIFO controller settings + boolSettings.insert(pair("run_length_encoding", true)); + + // Environment customization settings + boolSettings.insert(pair("restricted_action_set", false)); + intSettings.insert(pair("random_seed", 0)); + boolSettings.insert(pair("color_averaging", false)); + boolSettings.insert(pair("send_rgb", false)); + intSettings.insert(pair("frame_skip", 1)); + floatSettings.insert(pair("repeat_action_probability", 0.25)); + stringSettings.insert(pair("rom_name", "")); + + // Record settings + intSettings.insert(pair("fragsize", 64)); // fragsize to 64 ensures proper sound sync + stringSettings.insert(pair("record_screen_dir", "")); + stringSettings.insert(pair("record_sound_filename", "")); + + // Display Settings + boolSettings.insert(pair("display_screen", false)); + + for(map::iterator it = stringSettings.begin(); it != stringSettings.end(); it++) { + this->setString(it->first, it->second); + } + + for(map::iterator it = floatSettings.begin(); it != floatSettings.end(); it++) { + this->setFloat(it->first, it->second); + } + + for(map::iterator it = boolSettings.begin(); it != boolSettings.end(); it++) { + this->setBool(it->first, it->second); + } + + for(map::iterator it = intSettings.begin(); it != intSettings.end(); it++) { + this->setInt(it->first, it->second); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +template +void Settings::verifyVariableExistence(map dict, string key){ + if(dict.find(key) == dict.end()){ + throw std::runtime_error("The key " + key + " you are trying to set does not exist.\n"); + } +} diff --git a/atari_py/ale_interface/src/emucore/Settings.hxx b/atari_py/ale_interface/src/emucore/Settings.hxx index 175491e..98c2634 100644 --- a/atari_py/ale_interface/src/emucore/Settings.hxx +++ b/atari_py/ale_interface/src/emucore/Settings.hxx @@ -21,6 +21,9 @@ class OSystem; +#include +#include + #include "../common/Array.hxx" #include "m6502/src/bspf/src/bspf.hxx" @@ -162,6 +165,7 @@ class Settings */ void setSize(const std::string& key, const int value1, const int value2); + private: // Copy constructor isn't supported by this class so make it private Settings(const Settings&); @@ -177,6 +181,9 @@ class Settings str.substr(first, str.find_last_not_of(' ')-first+1); } + // Sets all of the ALE-specific default settings + void setDefaultSettings(); + protected: // The parent OSystem object OSystem* myOSystem; @@ -206,6 +213,15 @@ class Settings int pos = -1, bool useAsInitial = false); private: + //Maps containing all external settings an user can + //define and their respectives default values. + std::map intSettings; + std::map boolSettings; + std::map floatSettings; + std::map stringSettings; + template + void verifyVariableExistence(std::map dict, std::string key); + // Holds key,value pairs that are necessary for Stella to // function and must be saved on each program exit. SettingsArray myInternalSettings; diff --git a/atari_py/ale_interface/src/emucore/TIA.cxx b/atari_py/ale_interface/src/emucore/TIA.cxx index 43bcffa..535195f 100644 --- a/atari_py/ale_interface/src/emucore/TIA.cxx +++ b/atari_py/ale_interface/src/emucore/TIA.cxx @@ -34,6 +34,15 @@ using namespace std; #define HBLANK 68 +// c++98 compatibility +#if !defined(__intptr_t_defined) && !defined(_UINTPTR_T_DEFINED) && !defined(uintptr_t) +# if __WORDSIZE == 64 +typedef unsigned long int uintptr_t; +# else +typedef unsigned int uintptr_t; +# endif +#endif + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TIA::TIA(const Console& console, Settings& settings) : myConsole(console), diff --git a/atari_py/ale_interface/src/emucore/m6502/src/System.cxx b/atari_py/ale_interface/src/emucore/m6502/src/System.cxx index 90b72b6..ea27dd4 100644 --- a/atari_py/ale_interface/src/emucore/m6502/src/System.cxx +++ b/atari_py/ale_interface/src/emucore/m6502/src/System.cxx @@ -28,20 +28,13 @@ using namespace std; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -System::System(uInt16 n, uInt16 m) - : myAddressMask((1 << n) - 1), - myPageShift(m), - myPageMask((1 << m) - 1), - myNumberOfPages(1 << (n - m)), - myNumberOfDevices(0), +System::System() + : myNumberOfDevices(0), myM6502(0), myTIA(0), myCycles(0), myDataBusState(0) { - // Make sure the arguments are reasonable - assert((1 <= m) && (m <= n) && (n <= 16)); - // Allocate page table myPageAccessTable = new PageAccess[myNumberOfPages]; @@ -285,12 +278,7 @@ bool System::loadState(const string& md5sum, Deserializer& in) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -System::System(const System& s) - : myAddressMask(s.myAddressMask), - myPageShift(s.myPageShift), - myPageMask(s.myPageMask), - myNumberOfPages(s.myNumberOfPages) -{ +System::System(const System& s) { assert(false); } @@ -302,52 +290,6 @@ System& System::operator = (const System&) return *this; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 System::peek(uInt16 addr) -{ - PageAccess& access = myPageAccessTable[(addr & myAddressMask) >> myPageShift]; - - uInt8 result; - - // See if this page uses direct accessing or not - if(access.directPeekBase != 0) - { - result = *(access.directPeekBase + (addr & myPageMask)); - } - else - { - result = access.device->peek(addr); - } - -#ifdef DEBUGGER_SUPPORT - if(!myDataBusLocked) -#endif - myDataBusState = result; - - return result; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void System::poke(uInt16 addr, uInt8 value) -{ - PageAccess& access = myPageAccessTable[(addr & myAddressMask) >> myPageShift]; - - // See if this page uses direct accessing or not - if(access.directPokeBase != 0) - { - *(access.directPokeBase + (addr & myPageMask)) = value; - } - else - { - access.device->poke(addr, value); - } - -#ifdef DEBUGGER_SUPPORT - if(!myDataBusLocked) -#endif - myDataBusState = value; -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void System::lockDataBus() { diff --git a/atari_py/ale_interface/src/emucore/m6502/src/System.hxx b/atari_py/ale_interface/src/emucore/m6502/src/System.hxx index b6fb5eb..fdab5b2 100644 --- a/atari_py/ale_interface/src/emucore/m6502/src/System.hxx +++ b/atari_py/ale_interface/src/emucore/m6502/src/System.hxx @@ -53,13 +53,10 @@ class System { public: /** - Create a new system with an addressing space of 2^n bytes and - pages of 2^m bytes. - - @param n Log base 2 of the addressing space size - @param m Log base 2 of the page size + Create a new system with an addressing space of 2^13 bytes and + pages of 2^6 bytes. */ - System(uInt16 n, uInt16 m); + System(); /** Destructor @@ -187,7 +184,7 @@ class System */ uInt16 pageShift() const { - return myPageShift; + return myPageSize; } /** @@ -246,17 +243,57 @@ class System @return The byte at the specified address */ - uInt8 peek(uInt16 address); + inline uInt8 peek(uInt16 addr) + { + PageAccess& access = myPageAccessTable[(addr & myAddressMask) >> myPageSize]; + + uInt8 result; + + // See if this page uses direct accessing or not + if(access.directPeekBase != 0) + { + result = *(access.directPeekBase + (addr & myPageMask)); + } + else + { + result = access.device->peek(addr); + } + + #ifdef DEBUGGER_SUPPORT + if(!myDataBusLocked) + #endif + myDataBusState = result; + + return result; + } /** Change the byte at the specified address to the given value. No masking of the address occurs before it's sent to the device mapped at the address. - @param address The address where the value should be stored + @param addr The address where the value should be stored @param value The value to be stored at the address */ - void poke(uInt16 address, uInt8 value); + inline void poke(uInt16 addr, uInt8 value) { + PageAccess& access = myPageAccessTable[ + (addr & myAddressMask) >> myPageSize]; + + // See if this page uses direct accessing or not + if(access.directPokeBase != 0) + { + *(access.directPokeBase + (addr & myPageMask)) = value; + } + else + { + access.device->poke(addr, value); + } + + #ifdef DEBUGGER_SUPPORT + if(!myDataBusLocked) + #endif + myDataBusState = value; + } /** Lock/unlock the data bus. When the bus is locked, peek() and @@ -316,17 +353,20 @@ class System const PageAccess& getPageAccess(uInt16 page); private: - // Mask to apply to an address before accessing memory - const uInt16 myAddressMask; + // Log base 2 of the addressing space size. + static const uInt16 myAddressingSpace = 13; + + // Log base 2 of the page size. + static const uInt16 myPageSize = 6; - // Amount to shift an address by to determine what page it's on - const uInt16 myPageShift; + // Mask to apply to an address before accessing memory + static const uInt16 myAddressMask = (1 << myAddressingSpace) - 1; // Mask to apply to an address to obtain its page offset - const uInt16 myPageMask; + static const uInt16 myPageMask = (1 << myPageSize) - 1; // Number of pages in the system - const uInt16 myNumberOfPages; + static const uInt16 myNumberOfPages = 1 << (myAddressingSpace - myPageSize); // Pointer to a dynamically allocated array of PageAccess structures PageAccess* myPageAccessTable; diff --git a/atari_py/ale_interface/src/emucore/m6502/src/bspf/src/bspf.hxx b/atari_py/ale_interface/src/emucore/m6502/src/bspf/src/bspf.hxx index b387051..df174aa 100644 --- a/atari_py/ale_interface/src/emucore/m6502/src/bspf/src/bspf.hxx +++ b/atari_py/ale_interface/src/emucore/m6502/src/bspf/src/bspf.hxx @@ -56,20 +56,23 @@ typedef unsigned int uInt32; #endif // Defines to help with path handling -//ALE #if defined BSPF_UNIX -#define BSPF_PATH_SEPARATOR "/" -//ALE #elif (defined(BSPF_DOS) || defined(BSPF_WIN32) || defined(BSPF_OS2)) -//ALE #define BSPF_PATH_SEPARATOR "\\" -//ALE #elif defined BSPF_MAC_OSX -//ALE #define BSPF_PATH_SEPARATOR "/" -//ALE #elif defined BSPF_GP2X -//ALE #define BSPF_PATH_SEPARATOR "/" -//ALE #endif +#if defined(BSPF_UNIX) + #define BSPF_PATH_SEPARATOR "/" +#elif (defined(BSPF_DOS) || defined(BSPF_WIN32) || defined(BSPF_OS2)) + #define BSPF_PATH_SEPARATOR "\\" +#elif defined(BSPF_MAC_OSX) + #define BSPF_PATH_SEPARATOR "/" +#elif defined(BSPF_GP2X) + #define BSPF_PATH_SEPARATOR "/" +#else + #error "Unsupported platform or misconfigured" +#endif // I wish Windows had a complete POSIX layer #ifdef BSPF_WIN32 - #define BSPF_strcasecmp stricmp - #define BSPF_strncasecmp strnicmp +//#if defined(_MSC_VER) && (_MSC_VER < 1600) + #define BSPF_strcasecmp _stricmp + #define BSPF_strncasecmp _strnicmp #define BSPF_isblank(c) ((c == ' ') || (c == '\t')) #define BSPF_snprintf _snprintf #define BSPF_vsnprintf _vsnprintf diff --git a/atari_py/ale_interface/src/emucore/module.mk b/atari_py/ale_interface/src/emucore/module.mk index 54483c2..d290639 100644 --- a/atari_py/ale_interface/src/emucore/module.mk +++ b/atari_py/ale_interface/src/emucore/module.mk @@ -48,7 +48,6 @@ MODULE_OBJS := \ src/emucore/Switches.o \ src/emucore/TIA.o \ src/emucore/TIASnd.o \ - src/emucore/unzip.o \ MODULE_DIRS += \ src/emucore diff --git a/atari_py/ale_interface/src/emucore/rsynth/useconfig.h b/atari_py/ale_interface/src/emucore/rsynth/useconfig.h index f58f8af..69747e6 100644 --- a/atari_py/ale_interface/src/emucore/rsynth/useconfig.h +++ b/atari_py/ale_interface/src/emucore/rsynth/useconfig.h @@ -20,8 +20,12 @@ char *strchr (), *strrchr (); #endif #endif /* STDC_HEADERS */ -#if HAVE_UNISTD_H -#include +#ifdef _WIN32 +# include +#else +# if HAVE_UNISTD_H +# include +# endif #endif #if HAVE_LIBC_H diff --git a/atari_py/ale_interface/src/emucore/unzip.c b/atari_py/ale_interface/src/emucore/unzip.c deleted file mode 100644 index 7e4344e..0000000 --- a/atari_py/ale_interface/src/emucore/unzip.c +++ /dev/null @@ -1,1273 +0,0 @@ -/* unzip.c -- IO on .zip files using zlib - Version 0.15 beta, Mar 19th, 1998, - - Read unzip.h for more info -*/ - - -#include -#include -#include -#include - -#include "unzip.h" - -#ifdef STDC -# include -# include -# include -#endif -#ifdef NO_ERRNO_H - extern int errno; -#else -# include -#endif - -#ifndef local -# define local static -#endif -/* compile with -Dlocal if your debugger can't find static symbols */ - - - -#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ - !defined(CASESENSITIVITYDEFAULT_NO) -#define CASESENSITIVITYDEFAULT_NO -#endif - - -#ifndef UNZ_BUFSIZE -#define UNZ_BUFSIZE (16384) -#endif - -#ifndef UNZ_MAXFILENAMEINZIP -#define UNZ_MAXFILENAMEINZIP (256) -#endif - -#ifndef ALLOC -# define ALLOC(size) (malloc(size)) -#endif -#ifndef TRYFREE -# define TRYFREE(p) {if (p) free(p);} -#endif - -#define SIZECENTRALDIRITEM (0x2e) -#define SIZEZIPLOCALHEADER (0x1e) - - -/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ - -#ifndef SEEK_CUR -#define SEEK_CUR 1 -#endif - -#ifndef SEEK_END -#define SEEK_END 2 -#endif - -#ifndef SEEK_SET -#define SEEK_SET 0 -#endif - -const char unz_copyright[] = - " unzip 0.15 Copyright 1998 Gilles Vollant "; - -/* unz_file_info_interntal contain internal info about a file in zipfile*/ -typedef struct unz_file_info_internal_s -{ - uLong offset_curfile;/* relative offset of local header 4 bytes */ -} unz_file_info_internal; - - -/* file_in_zip_read_info_s contain internal information about a file in zipfile, - when reading and decompress it */ -typedef struct -{ - char *read_buffer; /* internal buffer for compressed data */ - z_stream stream; /* zLib stream structure for inflate */ - - uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ - uLong stream_initialised; /* flag set if stream structure is initialised*/ - - uLong offset_local_extrafield;/* offset of the local extra field */ - uInt size_local_extrafield;/* size of the local extra field */ - uLong pos_local_extrafield; /* position in the local extra field in read*/ - - uLong crc32; /* crc32 of all data uncompressed */ - uLong crc32_wait; /* crc32 we must obtain after decompress all */ - uLong rest_read_compressed; /* number of byte to be decompressed */ - uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ - FILE* file; /* io structore of the zipfile */ - uLong compression_method; /* compression method (0==store) */ - uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ -} file_in_zip_read_info_s; - - -/* unz_s contain internal information about the zipfile -*/ -typedef struct -{ - FILE* file; /* io structore of the zipfile */ - unz_global_info gi; /* public global information */ - uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ - uLong num_file; /* number of the current file in the zipfile*/ - uLong pos_in_central_dir; /* pos of the current file in the central dir*/ - uLong current_file_ok; /* flag about the usability of the current file*/ - uLong central_pos; /* position of the beginning of the central dir*/ - - uLong size_central_dir; /* size of the central directory */ - uLong offset_central_dir; /* offset of start of central directory with - respect to the starting disk number */ - - unz_file_info cur_file_info; /* public info about the current file in zip*/ - unz_file_info_internal cur_file_info_internal; /* private info about it*/ - file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current - file if we are decompressing it */ -} unz_s; - - -/* =========================================================================== - Read a byte from a gz_stream; update next_in and avail_in. Return EOF - for end of file. - IN assertion: the stream s has been sucessfully opened for reading. -*/ - - -local int unzlocal_getByte(FILE* fin, int* pi) -{ - unsigned char c; - int err = fread(&c, 1, 1, fin); - if (err==1) - { - *pi = (int)c; - return UNZ_OK; - } - else - { - if (ferror(fin)) - return UNZ_ERRNO; - else - return UNZ_EOF; - } -} - - -/* =========================================================================== - Reads a long in LSB order from the given gz_stream. Sets -*/ -local int unzlocal_getShort (FILE* fin,uLong* pX) -{ - uLong x ; - int i = 0; - int err; - - err = unzlocal_getByte(fin,&i); - x = (uLong)i; - - if (err==UNZ_OK) - err = unzlocal_getByte(fin,&i); - x += ((uLong)i)<<8; - - if (err==UNZ_OK) - *pX = x; - else - *pX = 0; - return err; -} - -local int unzlocal_getLong (FILE* fin,uLong* pX) -{ - uLong x ; - int i = 0; - int err; - - err = unzlocal_getByte(fin,&i); - x = (uLong)i; - - if (err==UNZ_OK) - err = unzlocal_getByte(fin,&i); - x += ((uLong)i)<<8; - - if (err==UNZ_OK) - err = unzlocal_getByte(fin,&i); - x += ((uLong)i)<<16; - - if (err==UNZ_OK) - err = unzlocal_getByte(fin,&i); - x += ((uLong)i)<<24; - - if (err==UNZ_OK) - *pX = x; - else - *pX = 0; - return err; -} - - -/* My own strcmpi / strcasecmp */ -local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) -{ - for (;;) - { - char c1=*(fileName1++); - char c2=*(fileName2++); - if ((c1>='a') && (c1<='z')) - c1 -= 0x20; - if ((c2>='a') && (c2<='z')) - c2 -= 0x20; - if (c1=='\0') - return ((c2=='\0') ? 0 : -1); - if (c2=='\0') - return 1; - if (c1c2) - return 1; - } -} - - -#ifdef CASESENSITIVITYDEFAULT_NO -#define CASESENSITIVITYDEFAULTVALUE 2 -#else -#define CASESENSITIVITYDEFAULTVALUE 1 -#endif - -#ifndef STRCMPCASENOSENTIVEFUNCTION -#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal -#endif - -/* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi - or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system - (like 1 on Unix, 2 on Windows) - -*/ -extern int ZEXPORT unzStringFileNameCompare - (const char* fileName1, const char* fileName2, int iCaseSensitivity) -{ - if (iCaseSensitivity==0) - iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; - - if (iCaseSensitivity==1) - return strcmp(fileName1,fileName2); - - return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); -} - -#define BUFREADCOMMENT (0x400) - -/* - Locate the Central directory of a zipfile (at the end, just before - the global comment) -*/ -local uLong unzlocal_SearchCentralDir(FILE* fin) -{ - unsigned char* buf; - uLong uSizeFile; - uLong uBackRead; - uLong uMaxBack=0xffff; /* maximum size of global comment */ - uLong uPosFound=0; - - if (fseek(fin,0,SEEK_END) != 0) - return 0; - - - uSizeFile = ftell( fin ); - - if (uMaxBack>uSizeFile) - uMaxBack = uSizeFile; - - buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); - if (buf==NULL) - return 0; - - uBackRead = 4; - while (uBackReaduMaxBack) - uBackRead = uMaxBack; - else - uBackRead+=BUFREADCOMMENT; - uReadPos = uSizeFile-uBackRead ; - - uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? - (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); - if (fseek(fin,uReadPos,SEEK_SET)!=0) - break; - - if (fread(buf,(uInt)uReadSize,1,fin)!=1) - break; - - for (i=(int)uReadSize-3; (i--)>0;) - if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && - ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) - { - uPosFound = uReadPos+i; - break; - } - - if (uPosFound!=0) - break; - } - TRYFREE(buf); - return uPosFound; -} - -/* - Open a Zip file. path contain the full pathname (by example, - on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer - "zlib/zlib109.zip". - If the zipfile cannot be opened (file don't exist or in not valid), the - return value is NULL. - Else, the return value is a unzFile Handle, usable with other function - of this unzip package. -*/ -extern unzFile ZEXPORT unzOpen(const char *path) -{ - unz_s us; - unz_s *s; - uLong central_pos,uL; - FILE * fin ; - - uLong number_disk; /* number of the current dist, used for - spaning ZIP, unsupported, always 0*/ - uLong number_disk_with_CD; /* number the the disk with central dir, used - for spaning ZIP, unsupported, always 0*/ - uLong number_entry_CD; /* total number of entries in - the central dir - (same than number_entry on nospan) */ - - int err=UNZ_OK; - - if (unz_copyright[0]!=' ') - return NULL; - - fin=fopen(path,"rb"); - if (fin==NULL) - return NULL; - - central_pos = unzlocal_SearchCentralDir(fin); - if (central_pos==0) - err=UNZ_ERRNO; - - if (fseek(fin,central_pos,SEEK_SET)!=0) - err=UNZ_ERRNO; - - /* the signature, already checked */ - if (unzlocal_getLong(fin,&uL)!=UNZ_OK) - err=UNZ_ERRNO; - - /* number of this disk */ - if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) - err=UNZ_ERRNO; - - /* number of the disk with the start of the central directory */ - if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) - err=UNZ_ERRNO; - - /* total number of entries in the central dir on this disk */ - if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) - err=UNZ_ERRNO; - - /* total number of entries in the central dir */ - if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) - err=UNZ_ERRNO; - - if ((number_entry_CD!=us.gi.number_entry) || - (number_disk_with_CD!=0) || - (number_disk!=0)) - err=UNZ_BADZIPFILE; - - /* size of the central directory */ - if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) - err=UNZ_ERRNO; - - /* offset of start of central directory with respect to the - starting disk number */ - if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) - err=UNZ_ERRNO; - - /* zipfile comment length */ - if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) - err=UNZ_ERRNO; - - if ((central_pospfile_in_zip_read!=NULL) - unzCloseCurrentFile(file); - - fclose(s->file); - TRYFREE(s); - return UNZ_OK; -} - - -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. */ -extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info) -{ - unz_s* s; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - *pglobal_info=s->gi; - return UNZ_OK; -} - - -/* - Translate date/time from Dos format to tm_unz (readable more easilty) -*/ -local void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) -{ - uLong uDate; - uDate = (uLong)(ulDosDate>>16); - ptm->tm_mday = (uInt)(uDate&0x1f) ; - ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; - ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; - - ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); - ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; - ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; -} - -/* - Get Info about the current file in the zipfile, with internal only info -*/ -local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, - unz_file_info *pfile_info, - unz_file_info_internal - *pfile_info_internal, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize)); - -local int unzlocal_GetCurrentFileInfoInternal - ( unzFile file, - unz_file_info *pfile_info, - unz_file_info_internal *pfile_info_internal, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize) -{ - unz_s* s; - unz_file_info file_info; - unz_file_info_internal file_info_internal; - int err=UNZ_OK; - uLong uMagic; - long lSeek=0; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) - err=UNZ_ERRNO; - - - /* we check the magic */ - if (err==UNZ_OK) - { - if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) - err=UNZ_ERRNO; - else if (uMagic!=0x02014b50) - err=UNZ_BADZIPFILE; - } - - if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) - err=UNZ_ERRNO; - - unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); - - if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) - err=UNZ_ERRNO; - - lSeek+=file_info.size_filename; - if ((err==UNZ_OK) && (szFileName!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_filename0) && (fileNameBufferSize>0)) - if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1) - err=UNZ_ERRNO; - lSeek -= uSizeRead; - } - - - if ((err==UNZ_OK) && (extraField!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) - lSeek=0; - else - err=UNZ_ERRNO; - } - if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) - if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1) - err=UNZ_ERRNO; - lSeek += file_info.size_file_extra - uSizeRead; - } - else - lSeek+=file_info.size_file_extra; - - - if ((err==UNZ_OK) && (szComment!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) - lSeek=0; - else - err=UNZ_ERRNO; - } - if ((file_info.size_file_comment>0) && (commentBufferSize>0)) - if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1) - err=UNZ_ERRNO; - lSeek+=file_info.size_file_comment - uSizeRead; - } - else - lSeek+=file_info.size_file_comment; - - if ((err==UNZ_OK) && (pfile_info!=NULL)) - *pfile_info=file_info; - - if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) - *pfile_info_internal=file_info_internal; - - return err; -} - - - -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. -*/ -extern int ZEXPORT unzGetCurrentFileInfo - ( unzFile file, - unz_file_info *pfile_info, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize - ) -{ - return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, - szFileName,fileNameBufferSize, - extraField,extraFieldBufferSize, - szComment,commentBufferSize); -} - -/* - Set the current file of the zipfile to the first file. - return UNZ_OK if there is no problem -*/ -extern int ZEXPORT unzGoToFirstFile (unzFile file) -{ - int err=UNZ_OK; - unz_s* s; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - s->pos_in_central_dir=s->offset_central_dir; - s->num_file=0; - err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return err; -} - - -/* - Set the current file of the zipfile to the next file. - return UNZ_OK if there is no problem - return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. -*/ -extern int ZEXPORT unzGoToNextFile (unzFile file) -{ - unz_s* s; - int err; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_END_OF_LIST_OF_FILE; - if (s->num_file+1==s->gi.number_entry) - return UNZ_END_OF_LIST_OF_FILE; - - s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + - s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; - s->num_file++; - err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return err; -} - - -/* - Try locate the file szFileName in the zipfile. - For the iCaseSensitivity signification, see unzipStringFileNameCompare - - return value : - UNZ_OK if the file is found. It becomes the current file. - UNZ_END_OF_LIST_OF_FILE if the file is not found -*/ -extern int ZEXPORT unzLocateFile - ( unzFile file, - const char *szFileName, - int iCaseSensitivity - ) -{ - unz_s* s; - int err; - - - uLong num_fileSaved; - uLong pos_in_central_dirSaved; - - - if (file==NULL) - return UNZ_PARAMERROR; - - if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) - return UNZ_PARAMERROR; - - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_END_OF_LIST_OF_FILE; - - num_fileSaved = s->num_file; - pos_in_central_dirSaved = s->pos_in_central_dir; - - err = unzGoToFirstFile(file); - - while (err == UNZ_OK) - { - char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; - unzGetCurrentFileInfo(file,NULL, - szCurrentFileName,sizeof(szCurrentFileName)-1, - NULL,0,NULL,0); - if (unzStringFileNameCompare(szCurrentFileName, - szFileName,iCaseSensitivity)==0) - return UNZ_OK; - err = unzGoToNextFile(file); - } - - s->num_file = num_fileSaved ; - s->pos_in_central_dir = pos_in_central_dirSaved ; - return err; -} - - -/* - Read the local header of the current zipfile - Check the coherency of the local header and info in the end of central - directory about this file - store in *piSizeVar the size of extra info in local header - (filename and size of extra field data) -*/ -local int unzlocal_CheckCurrentFileCoherencyHeader - ( unz_s* s, - uInt* piSizeVar, - uLong *poffset_local_extrafield, - uInt *psize_local_extrafield - ) -{ - uLong uMagic,uData,uFlags; - uLong size_filename; - uLong size_extra_field; - int err=UNZ_OK; - - *piSizeVar = 0; - *poffset_local_extrafield = 0; - *psize_local_extrafield = 0; - - if (fseek(s->file,s->cur_file_info_internal.offset_curfile + - s->byte_before_the_zipfile,SEEK_SET)!=0) - return UNZ_ERRNO; - - - if (err==UNZ_OK) - { - if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) - err=UNZ_ERRNO; - else if (uMagic!=0x04034b50) - err=UNZ_BADZIPFILE; - } - - if (unzlocal_getShort(s->file,&uData) != UNZ_OK) - err=UNZ_ERRNO; -/* - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) - err=UNZ_BADZIPFILE; -*/ - if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&uData) != UNZ_OK) - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) - err=UNZ_BADZIPFILE; - - if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && - (s->cur_file_info.compression_method!=Z_DEFLATED)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - - if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) - err=UNZ_BADZIPFILE; - - *piSizeVar += (uInt)size_filename; - - if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) - err=UNZ_ERRNO; - *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + - SIZEZIPLOCALHEADER + size_filename; - *psize_local_extrafield = (uInt)size_extra_field; - - *piSizeVar += (uInt)size_extra_field; - - return err; -} - -/* - Open for reading data the current file in the zipfile. - If there is no error and the file is opened, the return value is UNZ_OK. -*/ -extern int ZEXPORT unzOpenCurrentFile (unzFile file) -{ - int err=UNZ_OK; - int Store; - uInt iSizeVar; - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - uLong offset_local_extrafield; /* offset of the local extra field */ - uInt size_local_extrafield; /* size of the local extra field */ - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_PARAMERROR; - - if (s->pfile_in_zip_read != NULL) - unzCloseCurrentFile(file); - - if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, - &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) - return UNZ_BADZIPFILE; - - pfile_in_zip_read_info = (file_in_zip_read_info_s*) - ALLOC(sizeof(file_in_zip_read_info_s)); - if (pfile_in_zip_read_info==NULL) - return UNZ_INTERNALERROR; - - pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); - pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; - pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; - pfile_in_zip_read_info->pos_local_extrafield=0; - - if (pfile_in_zip_read_info->read_buffer==NULL) - { - TRYFREE(pfile_in_zip_read_info); - return UNZ_INTERNALERROR; - } - - pfile_in_zip_read_info->stream_initialised=0; - - if ((s->cur_file_info.compression_method!=0) && - (s->cur_file_info.compression_method!=Z_DEFLATED)) - err=UNZ_BADZIPFILE; - Store = s->cur_file_info.compression_method==0; - - pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; - pfile_in_zip_read_info->crc32=0; - pfile_in_zip_read_info->compression_method = - s->cur_file_info.compression_method; - pfile_in_zip_read_info->file=s->file; - pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; - - pfile_in_zip_read_info->stream.total_out = 0; - - if (!Store) - { - pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; - pfile_in_zip_read_info->stream.zfree = (free_func)0; - pfile_in_zip_read_info->stream.opaque = (voidpf)0; - - err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); - if (err == Z_OK) - pfile_in_zip_read_info->stream_initialised=1; - /* windowBits is passed < 0 to tell that there is no zlib header. - * Note that in this case inflate *requires* an extra "dummy" byte - * after the compressed stream in order to complete decompression and - * return Z_STREAM_END. - * In unzip, i don't wait absolutely Z_STREAM_END because I known the - * size of both compressed and uncompressed data - */ - } - pfile_in_zip_read_info->rest_read_compressed = - s->cur_file_info.compressed_size ; - pfile_in_zip_read_info->rest_read_uncompressed = - s->cur_file_info.uncompressed_size ; - - - pfile_in_zip_read_info->pos_in_zipfile = - s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + - iSizeVar; - - pfile_in_zip_read_info->stream.avail_in = (uInt)0; - - - s->pfile_in_zip_read = pfile_in_zip_read_info; - return UNZ_OK; -} - - -/* - Read bytes from the current file. - buf contain buffer where data must be copied - len the size of buf. - - return the number of byte copied if somes bytes are copied - return 0 if the end of file was reached - return <0 with error code if there is an error - (UNZ_ERRNO for IO error, or zLib error for uncompress error) -*/ -extern int ZEXPORT unzReadCurrentFile - ( unzFile file, - voidp buf, - unsigned len - ) -{ - int err=UNZ_OK; - uInt iRead = 0; - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - - if (pfile_in_zip_read_info->read_buffer == NULL) - return UNZ_END_OF_LIST_OF_FILE; - if (len==0) - return 0; - - pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; - - pfile_in_zip_read_info->stream.avail_out = (uInt)len; - - if (len>pfile_in_zip_read_info->rest_read_uncompressed) - pfile_in_zip_read_info->stream.avail_out = - (uInt)pfile_in_zip_read_info->rest_read_uncompressed; - - while (pfile_in_zip_read_info->stream.avail_out>0) - { - if ((pfile_in_zip_read_info->stream.avail_in==0) && - (pfile_in_zip_read_info->rest_read_compressed>0)) - { - uInt uReadThis = UNZ_BUFSIZE; - if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; - if (uReadThis == 0) - return UNZ_EOF; - if (fseek(pfile_in_zip_read_info->file, - pfile_in_zip_read_info->pos_in_zipfile + - pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) - return UNZ_ERRNO; - if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1, - pfile_in_zip_read_info->file)!=1) - return UNZ_ERRNO; - pfile_in_zip_read_info->pos_in_zipfile += uReadThis; - - pfile_in_zip_read_info->rest_read_compressed-=uReadThis; - - pfile_in_zip_read_info->stream.next_in = - (Bytef*)pfile_in_zip_read_info->read_buffer; - pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; - } - - if (pfile_in_zip_read_info->compression_method==0) - { - uInt uDoCopy,i ; - if (pfile_in_zip_read_info->stream.avail_out < - pfile_in_zip_read_info->stream.avail_in) - uDoCopy = pfile_in_zip_read_info->stream.avail_out ; - else - uDoCopy = pfile_in_zip_read_info->stream.avail_in ; - - for (i=0;istream.next_out+i) = - *(pfile_in_zip_read_info->stream.next_in+i); - - pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, - pfile_in_zip_read_info->stream.next_out, - uDoCopy); - pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; - pfile_in_zip_read_info->stream.avail_in -= uDoCopy; - pfile_in_zip_read_info->stream.avail_out -= uDoCopy; - pfile_in_zip_read_info->stream.next_out += uDoCopy; - pfile_in_zip_read_info->stream.next_in += uDoCopy; - pfile_in_zip_read_info->stream.total_out += uDoCopy; - iRead += uDoCopy; - } - else - { - uLong uTotalOutBefore,uTotalOutAfter; - const Bytef *bufBefore; - uLong uOutThis; - int flush=Z_SYNC_FLUSH; - - uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; - bufBefore = pfile_in_zip_read_info->stream.next_out; - - /* - if ((pfile_in_zip_read_info->rest_read_uncompressed == - pfile_in_zip_read_info->stream.avail_out) && - (pfile_in_zip_read_info->rest_read_compressed == 0)) - flush = Z_FINISH; - */ - err=inflate(&pfile_in_zip_read_info->stream,flush); - - uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; - uOutThis = uTotalOutAfter-uTotalOutBefore; - - pfile_in_zip_read_info->crc32 = - crc32(pfile_in_zip_read_info->crc32,bufBefore, - (uInt)(uOutThis)); - - pfile_in_zip_read_info->rest_read_uncompressed -= - uOutThis; - - iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); - - if (err==Z_STREAM_END) - return (iRead==0) ? UNZ_EOF : iRead; - if (err!=Z_OK) - break; - } - } - - if (err==Z_OK) - return iRead; - return err; -} - - -/* - Give the current position in uncompressed data -*/ -extern z_off_t ZEXPORT unztell (unzFile file) -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - return (z_off_t)pfile_in_zip_read_info->stream.total_out; -} - - -/* - return 1 if the end of file was reached, 0 elsewhere -*/ -extern int ZEXPORT unzeof (unzFile file) -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - if (pfile_in_zip_read_info->rest_read_uncompressed == 0) - return 1; - else - return 0; -} - - - -/* - Read extra field from the current file (opened by unzOpenCurrentFile) - This is the local-header version of the extra field (sometimes, there is - more info in the local-header version than in the central-header) - - if buf==NULL, it return the size of the local extra field that can be read - - if buf!=NULL, len is the size of the buffer, the extra header is copied in - buf. - the return value is the number of bytes copied in buf, or (if <0) - the error code -*/ -extern int ZEXPORT unzGetLocalExtrafield - ( unzFile file, - voidp buf, - unsigned len - ) -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - uInt read_now; - uLong size_to_read; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - size_to_read = (pfile_in_zip_read_info->size_local_extrafield - - pfile_in_zip_read_info->pos_local_extrafield); - - if (buf==NULL) - return (int)size_to_read; - - if (len>size_to_read) - read_now = (uInt)size_to_read; - else - read_now = (uInt)len ; - - if (read_now==0) - return 0; - - if (fseek(pfile_in_zip_read_info->file, - pfile_in_zip_read_info->offset_local_extrafield + - pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) - return UNZ_ERRNO; - - if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) - return UNZ_ERRNO; - - return (int)read_now; -} - -/* - Close the file in zip opened with unzipOpenCurrentFile - Return UNZ_CRCERROR if all the file was read but the CRC is not good -*/ -extern int ZEXPORT unzCloseCurrentFile (unzFile file) -{ - int err=UNZ_OK; - - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - - if (pfile_in_zip_read_info->rest_read_uncompressed == 0) - { - if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) - err=UNZ_CRCERROR; - } - - - TRYFREE(pfile_in_zip_read_info->read_buffer); - pfile_in_zip_read_info->read_buffer = NULL; - if (pfile_in_zip_read_info->stream_initialised) - inflateEnd(&pfile_in_zip_read_info->stream); - - pfile_in_zip_read_info->stream_initialised = 0; - TRYFREE(pfile_in_zip_read_info); - - s->pfile_in_zip_read=NULL; - - return err; -} - - -/* - Get the global comment string of the ZipFile, in the szComment buffer. - uSizeBuf is the size of the szComment buffer. - return the number of byte copied or an error code <0 -*/ -extern int ZEXPORT unzGetGlobalComment - ( unzFile file, - char *szComment, - uLong uSizeBuf - ) -{ - unz_s* s; - uLong uReadThis ; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - - uReadThis = uSizeBuf; - if (uReadThis>s->gi.size_comment) - uReadThis = s->gi.size_comment; - - if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0) - return UNZ_ERRNO; - - if (uReadThis>0) - { - *szComment='\0'; - if (fread(szComment,(uInt)uReadThis,1,s->file)!=1) - return UNZ_ERRNO; - } - - if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) - *(szComment+s->gi.size_comment)='\0'; - return (int)uReadThis; -} diff --git a/atari_py/ale_interface/src/emucore/unzip.h b/atari_py/ale_interface/src/emucore/unzip.h deleted file mode 100644 index 2c8ae7f..0000000 --- a/atari_py/ale_interface/src/emucore/unzip.h +++ /dev/null @@ -1,275 +0,0 @@ -/* unzip.h -- IO for uncompress .zip files using zlib - Version 0.15 beta, Mar 19th, 1998, - - Copyright (C) 1998 Gilles Vollant - - This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g - WinZip, InfoZip tools and compatible. - Encryption and multi volume ZipFile (span) are not supported. - Old compressions used by old PKZip 1.x are not supported - - THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE - CAN CHANGE IN FUTURE VERSION !! - I WAIT FEEDBACK at mail info@winimage.com - Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution - - Condition of use and distribution are the same than zlib : - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - -*/ -/* for more info about .ZIP format, see - ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip - PkWare has also a specification at : - ftp://ftp.pkware.com/probdesc.zip */ - -#ifndef _unz_H -#define _unz_H - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef _ZLIB_H -#include -#endif - -#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) -/* like the STRICT of WIN32, we define a pointer that cannot be converted - from (void*) without cast */ -typedef struct TagunzFile__ { int unused; } unzFile__; -typedef unzFile__ *unzFile; -#else -typedef voidp unzFile; -#endif - - -#define UNZ_OK (0) -#define UNZ_END_OF_LIST_OF_FILE (-100) -#define UNZ_ERRNO (Z_ERRNO) -#define UNZ_EOF (0) -#define UNZ_PARAMERROR (-102) -#define UNZ_BADZIPFILE (-103) -#define UNZ_INTERNALERROR (-104) -#define UNZ_CRCERROR (-105) - -/* tm_unz contain date/time info */ -typedef struct tm_unz_s -{ - uInt tm_sec; /* seconds after the minute - [0,59] */ - uInt tm_min; /* minutes after the hour - [0,59] */ - uInt tm_hour; /* hours since midnight - [0,23] */ - uInt tm_mday; /* day of the month - [1,31] */ - uInt tm_mon; /* months since January - [0,11] */ - uInt tm_year; /* years - [1980..2044] */ -} tm_unz; - -/* unz_global_info structure contain global data about the ZIPfile - These data comes from the end of central dir */ -typedef struct unz_global_info_s -{ - uLong number_entry; /* total number of entries in - the central dir on this disk */ - uLong size_comment; /* size of the global comment of the zipfile */ -} unz_global_info; - - -/* unz_file_info contain information about a file in the zipfile */ -typedef struct unz_file_info_s -{ - uLong version; /* version made by 2 bytes */ - uLong version_needed; /* version needed to extract 2 bytes */ - uLong flag; /* general purpose bit flag 2 bytes */ - uLong compression_method; /* compression method 2 bytes */ - uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ - uLong crc; /* crc-32 4 bytes */ - uLong compressed_size; /* compressed size 4 bytes */ - uLong uncompressed_size; /* uncompressed size 4 bytes */ - uLong size_filename; /* filename length 2 bytes */ - uLong size_file_extra; /* extra field length 2 bytes */ - uLong size_file_comment; /* file comment length 2 bytes */ - - uLong disk_num_start; /* disk number start 2 bytes */ - uLong internal_fa; /* internal file attributes 2 bytes */ - uLong external_fa; /* external file attributes 4 bytes */ - - tm_unz tmu_date; -} unz_file_info; - -extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, - const char* fileName2, - int iCaseSensitivity)); -/* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi - or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system - (like 1 on Unix, 2 on Windows) -*/ - - -extern unzFile ZEXPORT unzOpen OF((const char *path)); -/* - Open a Zip file. path contain the full pathname (by example, - on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer - "zlib/zlib111.zip". - If the zipfile cannot be opened (file don't exist or in not valid), the - return value is NULL. - Else, the return value is a unzFile Handle, usable with other function - of this unzip package. -*/ - -extern int ZEXPORT unzClose OF((unzFile file)); -/* - Close a ZipFile opened with unzipOpen. - If there is files inside the .Zip opened with unzOpenCurrentFile (see later), - these files MUST be closed with unzipCloseCurrentFile before call unzipClose. - return UNZ_OK if there is no problem. */ - -extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, - unz_global_info *pglobal_info)); -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. */ - - -extern int ZEXPORT unzGetGlobalComment OF((unzFile file, - char *szComment, - uLong uSizeBuf)); -/* - Get the global comment string of the ZipFile, in the szComment buffer. - uSizeBuf is the size of the szComment buffer. - return the number of byte copied or an error code <0 -*/ - - -/***************************************************************************/ -/* Unzip package allow you browse the directory of the zipfile */ - -extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); -/* - Set the current file of the zipfile to the first file. - return UNZ_OK if there is no problem -*/ - -extern int ZEXPORT unzGoToNextFile OF((unzFile file)); -/* - Set the current file of the zipfile to the next file. - return UNZ_OK if there is no problem - return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. -*/ - -extern int ZEXPORT unzLocateFile OF((unzFile file, - const char *szFileName, - int iCaseSensitivity)); -/* - Try locate the file szFileName in the zipfile. - For the iCaseSensitivity signification, see unzStringFileNameCompare - - return value : - UNZ_OK if the file is found. It becomes the current file. - UNZ_END_OF_LIST_OF_FILE if the file is not found -*/ - - -extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, - unz_file_info *pfile_info, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize)); -/* - Get Info about the current file - if pfile_info!=NULL, the *pfile_info structure will contain somes info about - the current file - if szFileName!=NULL, the filemane string will be copied in szFileName - (fileNameBufferSize is the size of the buffer) - if extraField!=NULL, the extra field information will be copied in extraField - (extraFieldBufferSize is the size of the buffer). - This is the Central-header version of the extra field - if szComment!=NULL, the comment string of the file will be copied in szComment - (commentBufferSize is the size of the buffer) -*/ - -/***************************************************************************/ -/* for reading the content of the current zipfile, you can open it, read data - from it, and close it (you can close it before reading all the file) - */ - -extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); -/* - Open for reading data the current file in the zipfile. - If there is no error, the return value is UNZ_OK. -*/ - -extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); -/* - Close the file in zip opened with unzOpenCurrentFile - Return UNZ_CRCERROR if all the file was read but the CRC is not good -*/ - - -extern int ZEXPORT unzReadCurrentFile OF((unzFile file, - voidp buf, - unsigned len)); -/* - Read bytes from the current file (opened by unzOpenCurrentFile) - buf contain buffer where data must be copied - len the size of buf. - - return the number of byte copied if somes bytes are copied - return 0 if the end of file was reached - return <0 with error code if there is an error - (UNZ_ERRNO for IO error, or zLib error for uncompress error) -*/ - -extern z_off_t ZEXPORT unztell OF((unzFile file)); -/* - Give the current position in uncompressed data -*/ - -extern int ZEXPORT unzeof OF((unzFile file)); -/* - return 1 if the end of file was reached, 0 elsewhere -*/ - -extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, - voidp buf, - unsigned len)); -/* - Read extra field from the current file (opened by unzOpenCurrentFile) - This is the local-header version of the extra field (sometimes, there is - more info in the local-header version than in the central-header) - - if buf==NULL, it return the size of the local extra field - - if buf!=NULL, len is the size of the buffer, the extra header is copied in - buf. - the return value is the number of bytes copied in buf, or (if <0) - the error code -*/ - -#ifdef __cplusplus -} -#endif - -#endif /* _unz_H */ diff --git a/atari_py/ale_interface/src/environment/ale_screen.hpp b/atari_py/ale_interface/src/environment/ale_screen.hpp index 021b293..ebb9a0d 100644 --- a/atari_py/ale_interface/src/environment/ale_screen.hpp +++ b/atari_py/ale_interface/src/environment/ale_screen.hpp @@ -11,14 +11,15 @@ * ale_screen.hpp * * A class that encapsulates an Atari 2600 screen. Code is provided inline for - * efficiency reasonss. + * efficiency reasons. * **************************************************************************** */ #ifndef __ALE_SCREEN_HPP__ #define __ALE_SCREEN_HPP__ -#include +#include +#include #include #include diff --git a/atari_py/ale_interface/src/environment/ale_state.cpp b/atari_py/ale_interface/src/environment/ale_state.cpp index 566b8e3..3df56e1 100644 --- a/atari_py/ale_interface/src/environment/ale_state.cpp +++ b/atari_py/ale_interface/src/environment/ale_state.cpp @@ -12,55 +12,85 @@ #include "ale_state.hpp" #include "../emucore/m6502/src/System.hxx" #include "../emucore/Event.hxx" +#include "../emucore/Deserializer.hxx" +#include "../emucore/Serializer.hxx" #include "../common/Constants.h" -using namespace std; +#include "../games/RomSettings.hpp" + +#include +#include /** Default constructor - loads settings from system */ ALEState::ALEState(): m_left_paddle(PADDLE_DEFAULT_VALUE), m_right_paddle(PADDLE_DEFAULT_VALUE), m_frame_number(0), - m_episode_frame_number(0){ + m_episode_frame_number(0), + m_mode(0), + m_difficulty(0) { } -ALEState::ALEState(const ALEState &rhs): +ALEState::ALEState(const ALEState &rhs, const std::string &serialized): m_left_paddle(rhs.m_left_paddle), m_right_paddle(rhs.m_right_paddle), m_frame_number(rhs.m_frame_number), m_episode_frame_number(rhs.m_episode_frame_number), - m_serialized_state(rhs.m_serialized_state) { + m_serialized_state(serialized), + m_mode(rhs.m_mode), + m_difficulty(rhs.m_difficulty) { } -ALEState::ALEState(const ALEState &rhs, std::string serialized): - m_left_paddle(rhs.m_left_paddle), - m_right_paddle(rhs.m_right_paddle), - m_frame_number(rhs.m_frame_number), - m_episode_frame_number(rhs.m_episode_frame_number), - m_serialized_state(serialized) { +ALEState::ALEState(const std::string &serialized) { + Deserializer des(serialized); + this->m_left_paddle = des.getInt(); + this->m_right_paddle = des.getInt(); + this->m_frame_number = des.getInt(); + this->m_episode_frame_number = des.getInt(); + this->m_mode = des.getInt(); + this->m_difficulty = des.getInt(); + this->m_serialized_state = des.getString(); } + /** Restores ALE to the given previously saved state. */ -void ALEState::load(OSystem* osystem, RomSettings* settings, std::string md5, const ALEState &rhs) { +void ALEState::load(OSystem* osystem, RomSettings* settings, std::string md5, const ALEState &rhs, + bool load_system) { assert(rhs.m_serialized_state.length() > 0); // Deserialize the stored string into the emulator state Deserializer deser(rhs.m_serialized_state); - + + // A primitive check to produce a meaningful error if this state does not contain osystem info. + if (deser.getBool() != load_system) + throw new std::runtime_error("Attempting to load an ALEState which does not contain " + "system information."); + osystem->console().system().loadState(md5, deser); + // If we have osystem data, load it as well + if (load_system) + osystem->loadState(deser); settings->loadState(deser); // Copy over other member variables m_left_paddle = rhs.m_left_paddle; m_right_paddle = rhs.m_right_paddle; - m_episode_frame_number = rhs.m_episode_frame_number; m_frame_number = rhs.m_frame_number; + m_episode_frame_number = rhs.m_episode_frame_number; + m_mode = rhs.m_mode; + m_difficulty = rhs.m_difficulty; } -ALEState ALEState::save(OSystem* osystem, RomSettings* settings, std::string md5) { +ALEState ALEState::save(OSystem* osystem, RomSettings* settings, std::string md5, + bool save_system) { // Use the emulator's built-in serialization to save the state - Serializer ser; + Serializer ser; + // We use 'save_system' as a check at load time. + ser.putBool(save_system); + osystem->console().system().saveState(md5, ser); + if (save_system) + osystem->saveState(ser); settings->saveState(ser); // Now make a copy of this state, also storing the emulator serialization @@ -68,13 +98,27 @@ ALEState ALEState::save(OSystem* osystem, RomSettings* settings, std::string md5 } void ALEState::incrementFrame(int steps /* = 1 */) { - m_frame_number+=steps; - m_episode_frame_number+=steps; + m_frame_number += steps; + m_episode_frame_number += steps; } -void ALEState::resetEpisodeFrameNumber(){ - m_episode_frame_number = 0; - } +void ALEState::resetEpisodeFrameNumber() { + m_episode_frame_number = 0; +} + +std::string ALEState::serialize() { + Serializer ser; + + ser.putInt(this->m_left_paddle); + ser.putInt(this->m_right_paddle); + ser.putInt(this->m_frame_number); + ser.putInt(this->m_episode_frame_number); + ser.putInt(this->m_mode); + ser.putInt(this->m_difficulty); + ser.putString(this->m_serialized_state); + + return ser.get_str(); +} @@ -228,6 +272,20 @@ void ALEState::applyActionPaddles(Event* event, int player_a_action, int player_ } } +void ALEState::pressSelect(Event* event) { + resetKeys(event); + event->set(Event::ConsoleSelect, 1); +} + +void ALEState::setDifficultySwitches(Event* event, unsigned int value) { + // The difficulty switches stay in their position from time step to time step. + // This means we don't call resetKeys() when setting their values. + event->set(Event::ConsoleLeftDifficultyA, value & 1); + event->set(Event::ConsoleLeftDifficultyB, !(value & 1)); + event->set(Event::ConsoleRightDifficultyA, (value & 2) >> 1); + event->set(Event::ConsoleRightDifficultyB, !((value & 2) >> 1)); +} + void ALEState::setActionJoysticks(Event* event, int player_a_action, int player_b_action) { // Reset keys resetKeys(event); @@ -417,20 +475,21 @@ void ALEState::setActionJoysticks(Event* event, int player_a_action, int player_ break; case RESET: event->set(Event::ConsoleReset, 1); - ale::Logger::Info << "Sending Reset..." << endl; + ale::Logger::Info << "Sending Reset..." << std::endl; break; default: - ale::Logger::Error << "Invalid Player B Action: " << player_b_action << endl; + ale::Logger::Error << "Invalid Player B Action: " << player_b_action << std::endl; exit(-1); } } /* *************************************************************************** Function resetKeys - Unpresses all control-relavant keys + Unpresses all control-relevant keys * ***************************************************************************/ void ALEState::resetKeys(Event* event) { event->set(Event::ConsoleReset, 0); + event->set(Event::ConsoleSelect, 0); event->set(Event::JoystickZeroFire, 0); event->set(Event::JoystickZeroUp, 0); event->set(Event::JoystickZeroDown, 0); @@ -445,12 +504,17 @@ void ALEState::resetKeys(Event* event) { // also reset paddle fire event->set(Event::PaddleZeroFire, 0); event->set(Event::PaddleOneFire, 0); + + // Set the difficulty switches accordingly for this time step. + setDifficultySwitches(event, m_difficulty); } bool ALEState::equals(ALEState &rhs) { return (rhs.m_serialized_state == this->m_serialized_state && rhs.m_left_paddle == this->m_left_paddle && rhs.m_right_paddle == this->m_right_paddle && - rhs.m_frame_number == this->m_frame_number) && - rhs.m_episode_frame_number == this->m_episode_frame_number; + rhs.m_frame_number == this->m_frame_number && + rhs.m_episode_frame_number == this->m_episode_frame_number && + rhs.m_mode == this->m_mode && + rhs.m_difficulty == this->m_difficulty); } diff --git a/atari_py/ale_interface/src/environment/ale_state.hpp b/atari_py/ale_interface/src/environment/ale_state.hpp index a7e226c..6dfc7a5 100644 --- a/atari_py/ale_interface/src/environment/ale_state.hpp +++ b/atari_py/ale_interface/src/environment/ale_state.hpp @@ -21,9 +21,10 @@ #include "../emucore/OSystem.hxx" #include "../emucore/Event.hxx" #include -#include "../games/RomSettings.hpp" #include "../common/Log.hpp" +class RomSettings; + #define PADDLE_DELTA 23000 // MGB Values taken from Paddles.cxx (Stella 3.3) - 1400000 * [5,235] / 255 #define PADDLE_MIN 27450 @@ -35,10 +36,11 @@ class ALEState { public: ALEState(); - // Copy constructor - ALEState(const ALEState &rhs); // Makes a copy of this state, also storing emulator information provided as a string - ALEState(const ALEState &rhs, std::string serialized); + ALEState(const ALEState &rhs, const std::string &serialized); + + // Restores a serialized ALEState + ALEState(const std::string &serialized); /** Resets the system to its start state. numResetSteps 'RESET' actions are taken after the * start. */ @@ -49,6 +51,9 @@ class ALEState { void resetPaddles(Event*); + //Apply the special select action + void pressSelect(Event* event_obj); + /** Applies paddle actions. This actually modifies the game state by updating the paddle * resistances. */ void applyActionPaddles(Event* event_obj, int player_a_action, int player_b_action); @@ -56,14 +61,31 @@ class ALEState { void setActionJoysticks(Event* event_obj, int player_a_action, int player_b_action); void incrementFrame(int steps = 1); - + void resetEpisodeFrameNumber(); - + //Get the frames executed so far - const int getFrameNumber() const { return m_frame_number; } + int getFrameNumber() const { return m_frame_number; } //Get the number of frames executed this episode. - const int getEpisodeFrameNumber() const { return m_episode_frame_number; } + int getEpisodeFrameNumber() const { return m_episode_frame_number; } + + /** set the difficulty according to the value. + * If the first bit is 1, then it will put the left difficulty switch to A (otherwise leave it on B) + * If the second bit is 1, then it will put the right difficulty switch to A (otherwise leave it on B) + */ + void setDifficulty(unsigned int value) { m_difficulty = value; } + + // Returns the current difficulty setting. + unsigned int getDifficulty() const { return m_difficulty; } + + //Save the current mode we are supposed to be in. + void setCurrentMode(game_mode_t value) { m_mode = value; } + + //Get the current mode we are in. + game_mode_t getCurrentMode() const { return m_mode; } + + std::string serialize(); protected: @@ -71,12 +93,14 @@ class ALEState { friend class StellaEnvironment; // The two methods below are meant to be used by StellaEnvironment. - /** Restores the environment to a previously saved state. */ - void load(OSystem* osystem, RomSettings* settings, std::string md5, const ALEState &rhs); + /** Restores the environment to a previously saved state. If load_system == true, we also + restore system-specific information (such as the RNG state). */ + void load(OSystem* osystem, RomSettings* settings, std::string md5, const ALEState &rhs, + bool load_system); /** Returns a "copy" of the current state, including the information necessary to restore - * the emulator. */ - ALEState save(OSystem* osystem, RomSettings* settings, std::string md5); + * the emulator. If save_system == true, this includes the RNG state. */ + ALEState save(OSystem* osystem, RomSettings* settings, std::string md5, bool save_system); /** Reset key presses */ void resetKeys(Event* event_obj); @@ -89,7 +113,10 @@ class ALEState { /** Calculates the Paddle resistance, based on the given x val */ int calcPaddleResistance(int x_val); - + + /** Applies the current difficulty setting, which is effectively part of the action */ + void setDifficultySwitches(Event* event_obj, unsigned int value); + private: int m_left_paddle; // Current value for the left-paddle int m_right_paddle; // Current value for the right-paddle @@ -99,8 +126,10 @@ class ALEState { std::string m_serialized_state; // The stored environment state, if this is a saved state + game_mode_t m_mode; //The current mode we are in + difficulty_t m_difficulty; //The current difficulty we are in + }; #endif // __ALE_STATE_HPP__ - diff --git a/atari_py/ale_interface/src/environment/phosphor_blend.cpp b/atari_py/ale_interface/src/environment/phosphor_blend.cpp index ea7b98e..c085bce 100644 --- a/atari_py/ale_interface/src/environment/phosphor_blend.cpp +++ b/atari_py/ale_interface/src/environment/phosphor_blend.cpp @@ -23,7 +23,7 @@ PhosphorBlend::PhosphorBlend(OSystem * osystem): // Taken from default Stella settings m_phosphor_blend_ratio = 77; - // makeAveragePalette(); + makeAveragePalette(); } void PhosphorBlend::process(ALEScreen& screen) { @@ -49,9 +49,9 @@ void PhosphorBlend::makeAveragePalette() { ColourPalette &palette = m_osystem->colourPalette(); - // Precompute the average RGB values for phosphor-averaged colors c1 and c2 - for (int c1 = 0; c1 < 256; c1++) { - for (int c2 = 0; c2 < 256; c2++) { + // Precompute the average RGB values for phosphor-averaged colors c1 and c2. + for (int c1 = 0; c1 < 256; c1 += 2) { + for (int c2 = 0; c2 < 256; c2 += 2) { int r1, g1, b1; int r2, g2, b2; palette.getRGB(c1, r1, g1, b1); @@ -64,7 +64,8 @@ void PhosphorBlend::makeAveragePalette() { } } - // Also make a RGB to NTSC color map + // Also make a RGB to NTSC color map. We drop the lowest two bits to speed + // the initialization a little. TODO(mgbellemare): Find a better solution. for (int r = 0; r < 256; r += 4) { for (int g = 0; g < 256; g += 4) { for (int b = 0; b < 256; b += 4) { @@ -72,7 +73,9 @@ void PhosphorBlend::makeAveragePalette() { int minDist = 256 * 3 + 1; int minIndex = -1; - for (int c1 = 0; c1 < 256; c1++) { + // Look for the closest NTSC value matching (r,g,b). Odd palette + // entries correspond to grayscale values and are ignored. + for (int c1 = 0; c1 < 256; c1 += 2) { // Get the RGB corresponding to c1 int r1, g1, b1; palette.getRGB(c1, r1, g1, b1); diff --git a/atari_py/ale_interface/src/environment/stella_environment.cpp b/atari_py/ale_interface/src/environment/stella_environment.cpp index 17dc689..1164e51 100644 --- a/atari_py/ale_interface/src/environment/stella_environment.cpp +++ b/atari_py/ale_interface/src/environment/stella_environment.cpp @@ -22,7 +22,7 @@ StellaEnvironment::StellaEnvironment(OSystem* osystem, RomSettings* settings): m_osystem(osystem), m_settings(settings), - // m_phosphor_blend(osystem), + m_phosphor_blend(osystem), m_screen(m_osystem->console().mediaSource().height(), m_osystem->console().mediaSource().width()), m_player_a_action(PLAYER_A_NOOP), @@ -56,7 +56,7 @@ StellaEnvironment::StellaEnvironment(OSystem* osystem, RomSettings* settings): ale::Logger::Info << "Recording screens to directory: " << recordDir << std::endl; // Create the screen exporter - m_screen_exporter.reset(new ScreenExporter(m_osystem->colourPalette(), recordDir)); + //Kojoley m_screen_exporter.reset(new ScreenExporter(m_osystem->colourPalette(), recordDir)); } } @@ -74,12 +74,18 @@ void StellaEnvironment::reset() { noopSteps = 60; emulate(PLAYER_A_NOOP, PLAYER_B_NOOP, noopSteps); - // reset for n steps - emulate(RESET, PLAYER_B_NOOP, m_num_reset_steps); + // Reset the emulator + softReset(); // reset the rom (after emulating, in case the NOOPs led to reward) m_settings->reset(); - + + StellaEnvironmentWrapper wrapper(*this); + + // Apply mode that was previously defined, then soft reset with this mode + m_settings->setMode(m_state.getCurrentMode(), m_osystem->console().system(), wrapper); + softReset(); + // Apply necessary actions specified by the rom itself ActionVect startingActions = m_settings->getStartingActions(); for (size_t i = 0; i < startingActions.size(); i++){ @@ -103,17 +109,20 @@ void StellaEnvironment::load() { m_saved_states.pop(); } -/** Returns a copy of the current emulator state. */ ALEState StellaEnvironment::cloneState() { - return m_state.save(m_osystem, m_settings, m_cartridge_md5); + return m_state.save(m_osystem, m_settings, m_cartridge_md5, false); } -/** Restores a previously saved copy of the state. */ void StellaEnvironment::restoreState(const ALEState& target_state) { - m_state.load(m_osystem, m_settings, m_cartridge_md5, target_state); - m_osystem->console().mediaSource().update(); - processScreen(); - processRAM(); + m_state.load(m_osystem, m_settings, m_cartridge_md5, target_state, false); +} + +ALEState StellaEnvironment::cloneSystemState() { + return m_state.save(m_osystem, m_settings, m_cartridge_md5, true); +} + +void StellaEnvironment::restoreSystemState(const ALEState& target_state) { + m_state.load(m_osystem, m_settings, m_cartridge_md5, target_state, true); } void StellaEnvironment::noopIllegalActions(Action & player_a_action, Action & player_b_action) { @@ -134,35 +143,49 @@ void StellaEnvironment::noopIllegalActions(Action & player_a_action, Action & pl } reward_t StellaEnvironment::act(Action player_a_action, Action player_b_action) { - + // Total reward received as we repeat the action reward_t sum_rewards = 0; + Random& rng = m_osystem->rng(); + // Apply the same action for a given number of times... note that act() will refuse to emulate // past the terminal state for (size_t i = 0; i < m_frame_skip; i++) { // Stochastically drop actions, according to m_repeat_action_probability - if (m_rand_gen.nextDouble() >= m_repeat_action_probability) + if (rng.nextDouble() >= m_repeat_action_probability) m_player_a_action = player_a_action; // @todo Possibly optimize by avoiding call to rand() when player B is "off" ? - if (m_rand_gen.nextDouble() >= m_repeat_action_probability) + if (rng.nextDouble() >= m_repeat_action_probability) m_player_b_action = player_b_action; // If so desired, request one frame's worth of sound (this does nothing if recording // is not enabled) m_osystem->sound().recordNextFrame(); + /* Kojoley + // Similarly record screen as needed + if (m_screen_exporter.get() != NULL) + m_screen_exporter->saveNext(m_screen); + Kojoley*/ + // Use the stored actions, which may or may not have changed this frame sum_rewards += oneStepAct(m_player_a_action, m_player_b_action); } - if (m_screen_exporter.get() != NULL) - m_screen_exporter->saveNext(m_screen); - return sum_rewards; } +/** This functions emulates a push on the reset button of the console */ +void StellaEnvironment::softReset() { + emulate(RESET, PLAYER_B_NOOP, m_num_reset_steps); + + // Reset previous actions to NOOP for correct action repeating + m_player_a_action = PLAYER_A_NOOP; + m_player_b_action = PLAYER_B_NOOP; +} + /** Applies the given actions (e.g. updating paddle positions when the paddle is used) * and performs one simulation step in Stella. */ reward_t StellaEnvironment::oneStepAct(Action player_a_action, Action player_b_action) { @@ -183,12 +206,31 @@ reward_t StellaEnvironment::oneStepAct(Action player_a_action, Action player_b_a return m_settings->getReward(); } -bool StellaEnvironment::isTerminal() { +bool StellaEnvironment::isTerminal() const { return (m_settings->isTerminal() || (m_max_num_frames_per_episode > 0 && m_state.getEpisodeFrameNumber() >= m_max_num_frames_per_episode)); } +void StellaEnvironment::pressSelect(size_t num_steps) { + m_state.pressSelect(m_osystem->event()); + for (size_t t = 0; t < num_steps; t++) { + m_osystem->console().mediaSource().update(); + } + processScreen(); + processRAM(); + emulate(PLAYER_A_NOOP, PLAYER_B_NOOP); + m_state.incrementFrame(); +} + +void StellaEnvironment::setDifficulty(difficulty_t value) { + m_state.setDifficulty(value); +} + +void StellaEnvironment::setMode(game_mode_t value) { + m_state.setCurrentMode(value); +} + void StellaEnvironment::emulate(Action player_a_action, Action player_b_action, size_t num_steps) { Event* event = m_osystem->event(); @@ -230,8 +272,7 @@ const ALEState& StellaEnvironment::getState() const { void StellaEnvironment::processScreen() { if (m_colour_averaging) { // Perform phosphor averaging; the blender stores its result in the given screen - // m_phosphor_blend.process(m_screen); - abort(); + m_phosphor_blend.process(m_screen); } else { // Copy screen over and we're done! diff --git a/atari_py/ale_interface/src/environment/stella_environment.hpp b/atari_py/ale_interface/src/environment/stella_environment.hpp index 03e49a5..45fa7a7 100644 --- a/atari_py/ale_interface/src/environment/stella_environment.hpp +++ b/atari_py/ale_interface/src/environment/stella_environment.hpp @@ -18,21 +18,20 @@ #ifndef __STELLA_ENVIRONMENT_HPP__ #define __STELLA_ENVIRONMENT_HPP__ -#include "ale_state.hpp" -#include "ale_screen.hpp" #include "ale_ram.hpp" +#include "ale_screen.hpp" +#include "ale_state.hpp" #include "phosphor_blend.hpp" -#include "../emucore/OSystem.hxx" +#include "stella_environment_wrapper.hpp" #include "../emucore/Event.hxx" +#include "../emucore/OSystem.hxx" #include "../games/RomSettings.hpp" -#include "../common/ScreenExporter.hpp" +#include "../common/Constants.h" #include "../common/Log.hpp" +//Kojoley #include "../common/ScreenExporter.hpp" #include -// This defines the number of "random" environments -#define NUM_RANDOM_ENVIRONMENTS (500) - class StellaEnvironment { public: StellaEnvironment(OSystem * system, RomSettings * settings); @@ -44,11 +43,18 @@ class StellaEnvironment { void save(); void load(); - /** Returns a copy of the current emulator state. */ + /** Returns a copy of the current emulator state. Note that this doesn't include + pseudorandomness, so that clone/restoreState are suitable for planning. */ ALEState cloneState(); /** Restores a previously saved copy of the state. */ void restoreState(const ALEState&); + /** Returns a copy of the current emulator state. This includes RNG state information, and + more generally should lead to exactly reproducibility. */ + ALEState cloneSystemState(); + /** Restores a previously saved copy of the state, including RNG state information. */ + void restoreSystemState(const ALEState&); + /** Applies the given actions (e.g. updating paddle positions when the paddle is used) * and performs one simulation step in Stella. Returns the resultant reward. When * frame skip is set to > 1, up the corresponding number of simulation steps are performed. @@ -57,8 +63,26 @@ class StellaEnvironment { */ reward_t act(Action player_a_action, Action player_b_action); + /** This functions emulates a push on the reset button of the console */ + void softReset(); + + /** Keep pressing the console select button for a given amount of time*/ + void pressSelect(size_t num_steps = 1); + + /** Set the difficulty according to the value. + * If the first bit is 1, then it will put the left difficulty switch to A (otherwise leave it on B) + * If the second bit is 1, then it will put the right difficulty switch to A (otherwise leave it on B) + * + * This change takes effect at the immediate next time step. + */ + void setDifficulty(difficulty_t value); + + /** Set the game mode according to the value. The new mode will not take effect until reset() is + * called */ + void setMode(game_mode_t value); + /** Returns true once we reach a terminal state */ - bool isTerminal(); + bool isTerminal() const; /** Accessor methods for the environment state. */ void setState(const ALEState & state); @@ -90,7 +114,7 @@ class StellaEnvironment { private: OSystem *m_osystem; RomSettings *m_settings; - // PhosphorBlend m_phosphor_blend; // For performing phosphor colour averaging, if so desired + PhosphorBlend m_phosphor_blend; // For performing phosphor colour averaging, if so desired std::string m_cartridge_md5; // Necessary for saving and loading emulator state std::stack m_saved_states; // States are saved on a stack @@ -107,8 +131,7 @@ class StellaEnvironment { int m_max_num_frames_per_episode; // Maxmimum number of frames per episode size_t m_frame_skip; // How many frames to emulate per act() float m_repeat_action_probability; // Stochasticity of the environment - std::auto_ptr m_screen_exporter; // Automatic screen recorder - Random m_rand_gen; + //Kojoley std::unique_ptr m_screen_exporter; // Automatic screen recorder // The last actions taken by our players Action m_player_a_action, m_player_b_action; diff --git a/atari_py/ale_interface/src/environment/stella_environment_wrapper.cpp b/atari_py/ale_interface/src/environment/stella_environment_wrapper.cpp new file mode 100644 index 0000000..0509e02 --- /dev/null +++ b/atari_py/ale_interface/src/environment/stella_environment_wrapper.cpp @@ -0,0 +1,18 @@ +#include "stella_environment.hpp" +#include "stella_environment_wrapper.hpp" + +StellaEnvironmentWrapper::StellaEnvironmentWrapper(StellaEnvironment &environment) : + m_environment(environment) { +} + +reward_t StellaEnvironmentWrapper::act(Action player_a_action, Action player_b_action) { + return m_environment.act(player_a_action, player_b_action); +} + +void StellaEnvironmentWrapper::softReset() { + m_environment.softReset(); +} + +void StellaEnvironmentWrapper::pressSelect(size_t num_steps) { + m_environment.pressSelect(num_steps); +} diff --git a/atari_py/ale_interface/src/environment/stella_environment_wrapper.hpp b/atari_py/ale_interface/src/environment/stella_environment_wrapper.hpp new file mode 100644 index 0000000..f21ae9c --- /dev/null +++ b/atari_py/ale_interface/src/environment/stella_environment_wrapper.hpp @@ -0,0 +1,38 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + * stella_environment_wrapper.hpp + * + * Method wrapper for StellaEnvironment. + * + **************************************************************************** */ + +#ifndef __STELLA_ENVIRONMENT_WRAPPER_HPP__ +#define __STELLA_ENVIRONMENT_WRAPPER_HPP__ + +#include "../common/Constants.h" + +class StellaEnvironment; + +class StellaEnvironmentWrapper { + // A wrapper for actions within the StellaEnvironment. + // Allows us to call environment methods without requiring to #include + // stella_environment.hpp. + public: + StellaEnvironmentWrapper(StellaEnvironment &environment); + reward_t act(Action player_a_action, Action player_b_action); + void softReset(); + void pressSelect(size_t num_steps = 1); + + StellaEnvironment &m_environment; +}; + +#endif // __STELLA_ENVIRONMENT_WRAPPER_HPP__ + diff --git a/atari_py/ale_interface/src/external/TinyMT/LICENSE.txt b/atari_py/ale_interface/src/external/TinyMT/LICENSE.txt deleted file mode 100644 index af03886..0000000 --- a/atari_py/ale_interface/src/external/TinyMT/LICENSE.txt +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima -University and The University of Tokyo. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of the Hiroshima University nor the names of - its contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/atari_py/ale_interface/src/external/TinyMT/tinymt32.c b/atari_py/ale_interface/src/external/TinyMT/tinymt32.c deleted file mode 100644 index ccc19f2..0000000 --- a/atari_py/ale_interface/src/external/TinyMT/tinymt32.c +++ /dev/null @@ -1,146 +0,0 @@ -/** - * @file tinymt32.c - * - * @brief Tiny Mersenne Twister only 127 bit internal state - * - * @author Mutsuo Saito (Hiroshima University) - * @author Makoto Matsumoto (The University of Tokyo) - * - * Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto, - * Hiroshima University and The University of Tokyo. - * All rights reserved. - * - * The 3-clause BSD License is applied to this software, see - * LICENSE.txt - */ -#include "tinymt32.h" -#define MIN_LOOP 8 -#define PRE_LOOP 8 - -/** - * This function represents a function used in the initialization - * by init_by_array - * @param x 32-bit integer - * @return 32-bit integer - */ -static uint32_t ini_func1(uint32_t x) { - return (x ^ (x >> 27)) * UINT32_C(1664525); -} - -/** - * This function represents a function used in the initialization - * by init_by_array - * @param x 32-bit integer - * @return 32-bit integer - */ -static uint32_t ini_func2(uint32_t x) { - return (x ^ (x >> 27)) * UINT32_C(1566083941); -} - -/** - * This function certificate the period of 2^127-1. - * @param random tinymt state vector. - */ -static void period_certification(tinymt32_t * random) { - if ((random->status[0] & TINYMT32_MASK) == 0 && - random->status[1] == 0 && - random->status[2] == 0 && - random->status[3] == 0) { - random->status[0] = 'T'; - random->status[1] = 'I'; - random->status[2] = 'N'; - random->status[3] = 'Y'; - } -} - -/** - * This function initializes the internal state array with a 32-bit - * unsigned integer seed. - * @param random tinymt state vector. - * @param seed a 32-bit unsigned integer used as a seed. - */ -void tinymt32_init(tinymt32_t * random, uint32_t seed) { - int i; - random->status[0] = seed; - random->status[1] = random->mat1; - random->status[2] = random->mat2; - random->status[3] = random->tmat; - for (i = 1; i < MIN_LOOP; i++) { - random->status[i & 3] ^= i + UINT32_C(1812433253) - * (random->status[(i - 1) & 3] - ^ (random->status[(i - 1) & 3] >> 30)); - } - period_certification(random); - for (i = 0; i < PRE_LOOP; i++) { - tinymt32_next_state(random); - } -} - -/** - * This function initializes the internal state array, - * with an array of 32-bit unsigned integers used as seeds - * @param random tinymt state vector. - * @param init_key the array of 32-bit integers, used as a seed. - * @param key_length the length of init_key. - */ -void tinymt32_init_by_array(tinymt32_t * random, uint32_t init_key[], - int key_length) { - const int lag = 1; - const int mid = 1; - const int size = 4; - int i, j; - int count; - uint32_t r; - uint32_t * st = &random->status[0]; - - st[0] = 0; - st[1] = random->mat1; - st[2] = random->mat2; - st[3] = random->tmat; - if (key_length + 1 > MIN_LOOP) { - count = key_length + 1; - } else { - count = MIN_LOOP; - } - r = ini_func1(st[0] ^ st[mid % size] - ^ st[(size - 1) % size]); - st[mid % size] += r; - r += key_length; - st[(mid + lag) % size] += r; - st[0] = r; - count--; - for (i = 1, j = 0; (j < count) && (j < key_length); j++) { - r = ini_func1(st[i % size] - ^ st[(i + mid) % size] - ^ st[(i + size - 1) % size]); - st[(i + mid) % size] += r; - r += init_key[j] + i; - st[(i + mid + lag) % size] += r; - st[i % size] = r; - i = (i + 1) % size; - } - for (; j < count; j++) { - r = ini_func1(st[i % size] - ^ st[(i + mid) % size] - ^ st[(i + size - 1) % size]); - st[(i + mid) % size] += r; - r += i; - st[(i + mid + lag) % size] += r; - st[i % size] = r; - i = (i + 1) % size; - } - for (j = 0; j < size; j++) { - r = ini_func2(st[i % size] - + st[(i + mid) % size] - + st[(i + size - 1) % size]); - st[(i + mid) % size] ^= r; - r -= i; - st[(i + mid + lag) % size] ^= r; - st[i % size] = r; - i = (i + 1) % size; - } - period_certification(random); - for (i = 0; i < PRE_LOOP; i++) { - tinymt32_next_state(random); - } -} diff --git a/atari_py/ale_interface/src/external/TinyMT/tinymt32.h b/atari_py/ale_interface/src/external/TinyMT/tinymt32.h deleted file mode 100644 index 942581b..0000000 --- a/atari_py/ale_interface/src/external/TinyMT/tinymt32.h +++ /dev/null @@ -1,247 +0,0 @@ -#ifndef TINYMT32_H -#define TINYMT32_H -/** - * @file tinymt32.h - * - * @brief Tiny Mersenne Twister only 127 bit internal state - * - * @author Mutsuo Saito (Hiroshima University) - * @author Makoto Matsumoto (University of Tokyo) - * - * Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto, - * Hiroshima University and The University of Tokyo. - * All rights reserved. - * - * The 3-clause BSD License is applied to this software, see - * LICENSE.txt - */ - -#include -#include - -#define TINYMT32_MEXP 127 -#define TINYMT32_SH0 1 -#define TINYMT32_SH1 10 -#define TINYMT32_SH8 8 -#define TINYMT32_MASK UINT32_C(0x7fffffff) -#define TINYMT32_MUL (1.0f / 16777216.0f) - -#if defined(__cplusplus) -extern "C" { -#endif - -/** - * tinymt32 internal state vector and parameters - */ -struct TINYMT32_T { - uint32_t status[4]; - uint32_t mat1; - uint32_t mat2; - uint32_t tmat; -}; - -typedef struct TINYMT32_T tinymt32_t; - -void tinymt32_init(tinymt32_t * random, uint32_t seed); -void tinymt32_init_by_array(tinymt32_t * random, uint32_t init_key[], - int key_length); - -#if defined(__GNUC__) -/** - * This function always returns 127 - * @param random not used - * @return always 127 - */ -inline static int tinymt32_get_mexp( - tinymt32_t * random __attribute__((unused))) { - return TINYMT32_MEXP; -} -#else -inline static int tinymt32_get_mexp(tinymt32_t * random) { - return TINYMT32_MEXP; -} -#endif - -/** - * This function changes internal state of tinymt32. - * Users should not call this function directly. - * @param random tinymt internal status - */ -inline static void tinymt32_next_state(tinymt32_t * random) { - uint32_t x; - uint32_t y; - - y = random->status[3]; - x = (random->status[0] & TINYMT32_MASK) - ^ random->status[1] - ^ random->status[2]; - x ^= (x << TINYMT32_SH0); - y ^= (y >> TINYMT32_SH0) ^ x; - random->status[0] = random->status[1]; - random->status[1] = random->status[2]; - random->status[2] = x ^ (y << TINYMT32_SH1); - random->status[3] = y; - random->status[1] ^= -((int32_t)(y & 1)) & random->mat1; - random->status[2] ^= -((int32_t)(y & 1)) & random->mat2; -} - -/** - * This function outputs 32-bit unsigned integer from internal state. - * Users should not call this function directly. - * @param random tinymt internal status - * @return 32-bit unsigned pseudorandom number - */ -inline static uint32_t tinymt32_temper(tinymt32_t * random) { - uint32_t t0, t1; - t0 = random->status[3]; -#if defined(LINEARITY_CHECK) - t1 = random->status[0] - ^ (random->status[2] >> TINYMT32_SH8); -#else - t1 = random->status[0] - + (random->status[2] >> TINYMT32_SH8); -#endif - t0 ^= t1; - t0 ^= -((int32_t)(t1 & 1)) & random->tmat; - return t0; -} - -/** - * This function outputs floating point number from internal state. - * Users should not call this function directly. - * @param random tinymt internal status - * @return floating point number r (1.0 <= r < 2.0) - */ -inline static float tinymt32_temper_conv(tinymt32_t * random) { - uint32_t t0, t1; - union { - uint32_t u; - float f; - } conv; - - t0 = random->status[3]; -#if defined(LINEARITY_CHECK) - t1 = random->status[0] - ^ (random->status[2] >> TINYMT32_SH8); -#else - t1 = random->status[0] - + (random->status[2] >> TINYMT32_SH8); -#endif - t0 ^= t1; - conv.u = ((t0 ^ (-((int32_t)(t1 & 1)) & random->tmat)) >> 9) - | UINT32_C(0x3f800000); - return conv.f; -} - -/** - * This function outputs floating point number from internal state. - * Users should not call this function directly. - * @param random tinymt internal status - * @return floating point number r (1.0 < r < 2.0) - */ -inline static float tinymt32_temper_conv_open(tinymt32_t * random) { - uint32_t t0, t1; - union { - uint32_t u; - float f; - } conv; - - t0 = random->status[3]; -#if defined(LINEARITY_CHECK) - t1 = random->status[0] - ^ (random->status[2] >> TINYMT32_SH8); -#else - t1 = random->status[0] - + (random->status[2] >> TINYMT32_SH8); -#endif - t0 ^= t1; - conv.u = ((t0 ^ (-((int32_t)(t1 & 1)) & random->tmat)) >> 9) - | UINT32_C(0x3f800001); - return conv.f; -} - -/** - * This function outputs 32-bit unsigned integer from internal state. - * @param random tinymt internal status - * @return 32-bit unsigned integer r (0 <= r < 2^32) - */ -inline static uint32_t tinymt32_generate_uint32(tinymt32_t * random) { - tinymt32_next_state(random); - return tinymt32_temper(random); -} - -/** - * This function outputs floating point number from internal state. - * This function is implemented using multiplying by (1 / 2^24). - * floating point multiplication is faster than using union trick in - * my Intel CPU. - * @param random tinymt internal status - * @return floating point number r (0.0 <= r < 1.0) - */ -inline static float tinymt32_generate_float(tinymt32_t * random) { - tinymt32_next_state(random); - return (tinymt32_temper(random) >> 8) * TINYMT32_MUL; -} - -/** - * This function outputs floating point number from internal state. - * This function is implemented using union trick. - * @param random tinymt internal status - * @return floating point number r (1.0 <= r < 2.0) - */ -inline static float tinymt32_generate_float12(tinymt32_t * random) { - tinymt32_next_state(random); - return tinymt32_temper_conv(random); -} - -/** - * This function outputs floating point number from internal state. - * This function is implemented using union trick. - * @param random tinymt internal status - * @return floating point number r (0.0 <= r < 1.0) - */ -inline static float tinymt32_generate_float01(tinymt32_t * random) { - tinymt32_next_state(random); - return tinymt32_temper_conv(random) - 1.0f; -} - -/** - * This function outputs floating point number from internal state. - * This function may return 1.0 and never returns 0.0. - * @param random tinymt internal status - * @return floating point number r (0.0 < r <= 1.0) - */ -inline static float tinymt32_generate_floatOC(tinymt32_t * random) { - tinymt32_next_state(random); - return 1.0f - tinymt32_generate_float(random); -} - -/** - * This function outputs floating point number from internal state. - * This function returns neither 0.0 nor 1.0. - * @param random tinymt internal status - * @return floating point number r (0.0 < r < 1.0) - */ -inline static float tinymt32_generate_floatOO(tinymt32_t * random) { - tinymt32_next_state(random); - return tinymt32_temper_conv_open(random) - 1.0f; -} - -/** - * This function outputs double precision floating point number from - * internal state. The returned value has 32-bit precision. - * In other words, this function makes one double precision floating point - * number from one 32-bit unsigned integer. - * @param random tinymt internal status - * @return floating point number r (0.0 < r <= 1.0) - */ -inline static double tinymt32_generate_32double(tinymt32_t * random) { - tinymt32_next_state(random); - return tinymt32_temper(random) * (1.0 / 4294967296.0); -} - -#if defined(__cplusplus) -} -#endif - -#endif diff --git a/atari_py/ale_interface/src/external/module.mk b/atari_py/ale_interface/src/external/module.mk deleted file mode 100644 index 9e25b44..0000000 --- a/atari_py/ale_interface/src/external/module.mk +++ /dev/null @@ -1,10 +0,0 @@ -MODULE := src/external - -MODULE_OBJS := \ - src/external/TinyMT/tinymt32.o \ - -MODULE_DIRS += \ - src/external/TinyMT - -# Include common rules -include $(srcdir)/common.rules diff --git a/atari_py/ale_interface/src/games/RomSettings.cpp b/atari_py/ale_interface/src/games/RomSettings.cpp index a4d91e3..923df09 100644 --- a/atari_py/ale_interface/src/games/RomSettings.cpp +++ b/atari_py/ale_interface/src/games/RomSettings.cpp @@ -17,6 +17,9 @@ */ #include "RomSettings.hpp" +RomSettings::RomSettings() {} + + bool RomSettings::isLegal(const Action& a) const { return true; } @@ -42,5 +45,20 @@ ActionVect RomSettings::getAllActions() { } ActionVect RomSettings::getStartingActions() { - return ActionVect(); + return ActionVect(); } + +ModeVect RomSettings::getAvailableModes() { + return ModeVect(1, 0); +}; + +void RomSettings::setMode(game_mode_t m, System&, StellaEnvironmentWrapper&) { + //By default, 0 is the only available mode + if(m != 0) { + throw std::runtime_error("This mode is not currently available for this game"); + } +} + +DifficultyVect RomSettings::getAvailableDifficulties() { + return DifficultyVect(1, 0); +}; diff --git a/atari_py/ale_interface/src/games/RomSettings.hpp b/atari_py/ale_interface/src/games/RomSettings.hpp index c7fc4c4..6180f59 100644 --- a/atari_py/ale_interface/src/games/RomSettings.hpp +++ b/atari_py/ale_interface/src/games/RomSettings.hpp @@ -33,19 +33,27 @@ #ifndef __ROMSETTINGS_HPP__ #define __ROMSETTINGS_HPP__ +#include +#include +#include + #include "../common/Constants.h" #include "../emucore/Serializer.hxx" #include "../emucore/Deserializer.hxx" +#include "../environment/stella_environment_wrapper.hpp" class System; - // rom support interface -struct RomSettings { +class RomSettings { + +public: + RomSettings(); + virtual ~RomSettings() {} // reset - virtual void reset() = 0; + virtual void reset(){}; // is end of game virtual bool isTerminal() const = 0; @@ -75,7 +83,7 @@ struct RomSettings { virtual bool isLegal(const Action &a) const; // Remaining lives. - virtual const int lives() { return isTerminal() ? 0 : 1; } + virtual int lives() { return isTerminal() ? 0 : 1; } // Returns a restricted (minimal) set of actions. If not overriden, this is all actions. virtual ActionVect getMinimalActionSet(); @@ -86,6 +94,19 @@ struct RomSettings { // Returns a list of actions that are required to start the game. // By default this is an empty list. virtual ActionVect getStartingActions(); + + // Returns a list of mode that the game can be played in. + // By default, there is only one available mode. + virtual ModeVect getAvailableModes(); + + // Set the mode of the game. The given mode must be + // one returned by the previous function. + virtual void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // Returns a list of difficulties that the game can be played in. + // By default, there is only one available difficulty. + virtual DifficultyVect getAvailableDifficulties(); }; diff --git a/atari_py/ale_interface/src/games/RomUtils.cpp b/atari_py/ale_interface/src/games/RomUtils.cpp index ad629f0..1c70184 100644 --- a/atari_py/ale_interface/src/games/RomUtils.cpp +++ b/atari_py/ale_interface/src/games/RomUtils.cpp @@ -29,7 +29,6 @@ int readRam(const System* system, int offset) { return sys->peek((offset & 0x7F) + 0x80); } - /* extracts a decimal value from a byte */ int getDecimalScore(int index, const System* system) { diff --git a/atari_py/ale_interface/src/games/Roms.cpp b/atari_py/ale_interface/src/games/Roms.cpp index 084ca63..5b1bafe 100644 --- a/atari_py/ale_interface/src/games/Roms.cpp +++ b/atari_py/ale_interface/src/games/Roms.cpp @@ -10,10 +10,10 @@ * ***************************************************************************** */ #include "Roms.hpp" -#include "RomSettings.hpp" #include "RomUtils.hpp" // include the game implementations +#include "supported/Adventure.hpp" #include "supported/AirRaid.hpp" #include "supported/Alien.hpp" #include "supported/Amidar.hpp" @@ -34,22 +34,32 @@ #include "supported/CrazyClimber.hpp" #include "supported/Defender.hpp" #include "supported/DemonAttack.hpp" +#include "supported/DonkeyKong.hpp" #include "supported/DoubleDunk.hpp" #include "supported/ElevatorAction.hpp" #include "supported/Enduro.hpp" #include "supported/FishingDerby.hpp" #include "supported/Freeway.hpp" +#include "supported/Frogger.hpp" #include "supported/Frostbite.hpp" +#include "supported/Galaxian.hpp" #include "supported/Gopher.hpp" #include "supported/Gravitar.hpp" #include "supported/Hero.hpp" #include "supported/IceHockey.hpp" #include "supported/JamesBond.hpp" #include "supported/JourneyEscape.hpp" +#include "supported/Kaboom.hpp" #include "supported/Kangaroo.hpp" +#include "supported/Koolaid.hpp" +#include "supported/KeystoneKapers.hpp" +#include "supported/Kingkong.hpp" #include "supported/Krull.hpp" #include "supported/KungFuMaster.hpp" +#include "supported/LaserGates.hpp" +#include "supported/LostLuggage.hpp" #include "supported/MontezumaRevenge.hpp" +#include "supported/MrDo.hpp" #include "supported/MsPacman.hpp" #include "supported/NameThisGame.hpp" #include "supported/Phoenix.hpp" @@ -62,12 +72,16 @@ #include "supported/RoadRunner.hpp" #include "supported/RoboTank.hpp" #include "supported/Seaquest.hpp" +#include "supported/SirLancelot.hpp" #include "supported/Skiing.hpp" #include "supported/Solaris.hpp" #include "supported/SpaceInvaders.hpp" #include "supported/StarGunner.hpp" #include "supported/Tennis.hpp" +#include "supported/Tetris.hpp" #include "supported/TimePilot.hpp" +#include "supported/Turmoil.hpp" +#include "supported/Trondead.hpp" #include "supported/Tutankham.hpp" #include "supported/UpNDown.hpp" #include "supported/Venture.hpp" @@ -76,9 +90,12 @@ #include "supported/YarsRevenge.hpp" #include "supported/Zaxxon.hpp" +#include + /* list of supported games */ static const RomSettings *roms[] = { + new AdventureSettings(), new AirRaidSettings(), new AlienSettings(), new AmidarSettings(), @@ -99,22 +116,32 @@ static const RomSettings *roms[] = { new CrazyClimberSettings(), new DefenderSettings(), new DemonAttackSettings(), + new DonkeyKongSettings(), new DoubleDunkSettings(), new ElevatorActionSettings(), new EnduroSettings(), new FishingDerbySettings(), new FreewaySettings(), + new FroggerSettings(), new FrostbiteSettings(), + new GalaxianSettings(), new GopherSettings(), new GravitarSettings(), new HeroSettings(), new IceHockeySettings(), new JamesBondSettings(), new JourneyEscapeSettings(), + new KaboomSettings(), new KangarooSettings(), + new KoolaidSettings(), + new KeystoneKapersSettings(), + new KingkongSettings(), new KrullSettings(), new KungFuMasterSettings(), + new LaserGatesSettings(), + new LostLuggageSettings(), new MontezumaRevengeSettings(), + new MrDoSettings(), new MsPacmanSettings(), new NameThisGameSettings(), new PhoenixSettings(), @@ -127,12 +154,16 @@ static const RomSettings *roms[] = { new RoadRunnerSettings(), new RoboTankSettings(), new SeaquestSettings(), + new SirLancelotSettings(), new SkiingSettings(), new SolarisSettings(), new SpaceInvadersSettings(), new StarGunnerSettings(), new TennisSettings(), + new TetrisSettings(), new TimePilotSettings(), + new TurmoilSettings(), + new TrondeadSettings(), new TutankhamSettings(), new UpNDownSettings(), new VentureSettings(), @@ -144,16 +175,9 @@ static const RomSettings *roms[] = { /* looks for the RL wrapper corresponding to a particular rom title */ -RomSettings *buildRomRLWrapper(const std::string &rom) { - - size_t slash_ind = rom.find_last_of("/\\"); - std::string rom_str = rom.substr(slash_ind + 1); - size_t dot_idx = rom_str.find_first_of("."); - rom_str = rom_str.substr(0, dot_idx); - std::transform(rom_str.begin(), rom_str.end(), rom_str.begin(), ::tolower); - +RomSettings *buildRomRLWrapper(const std::string &rom_name) { for (size_t i=0; i < sizeof(roms)/sizeof(roms[0]); i++) { - if (rom_str == roms[i]->rom()) return roms[i]->clone(); + if (rom_name == roms[i]->rom()) return roms[i]->clone(); } return NULL; diff --git a/atari_py/ale_interface/src/games/Roms.hpp b/atari_py/ale_interface/src/games/Roms.hpp index 056906d..6fb742b 100644 --- a/atari_py/ale_interface/src/games/Roms.hpp +++ b/atari_py/ale_interface/src/games/Roms.hpp @@ -12,13 +12,13 @@ #ifndef __ROMS_HPP__ #define __ROMS_HPP__ -#include - -struct RomSettings; +#include "RomSettings.hpp" +#include +#include // looks for the RL wrapper corresponding to a particular rom title -extern RomSettings *buildRomRLWrapper(const std::string &rom); +extern RomSettings *buildRomRLWrapper(const std::string &rom_name); #endif // __ROMS_HPP__ diff --git a/atari_py/ale_interface/src/games/module.mk b/atari_py/ale_interface/src/games/module.mk index 3af60e7..1e2edd0 100644 --- a/atari_py/ale_interface/src/games/module.mk +++ b/atari_py/ale_interface/src/games/module.mk @@ -4,6 +4,7 @@ MODULE_OBJS := \ src/games/Roms.o \ src/games/RomSettings.o \ src/games/RomUtils.o \ + src/games/supported/Adventure.o \ src/games/supported/AirRaid.o \ src/games/supported/Alien.o \ src/games/supported/Amidar.o \ @@ -24,22 +25,32 @@ MODULE_OBJS := \ src/games/supported/CrazyClimber.o \ src/games/supported/Defender.o \ src/games/supported/DemonAttack.o \ + src/games/supported/DonkeyKong.o \ src/games/supported/DoubleDunk.o \ src/games/supported/ElevatorAction.o \ src/games/supported/Enduro.o \ src/games/supported/FishingDerby.o \ src/games/supported/Freeway.o \ + src/games/supported/Frogger.o \ src/games/supported/Frostbite.o \ + src/games/supported/Galaxian.o \ src/games/supported/Gopher.o \ src/games/supported/Gravitar.o \ src/games/supported/Hero.o \ src/games/supported/IceHockey.o \ src/games/supported/JamesBond.o \ src/games/supported/JourneyEscape.o \ + src/games/supported/Kaboom.o \ src/games/supported/Kangaroo.o \ + src/games/supported/Koolaid.o \ + src/games/supported/KeystoneKapers.o \ + src/games/supported/Kingkong.o \ src/games/supported/Krull.o \ src/games/supported/KungFuMaster.o \ + src/games/supported/LaserGates.o \ + src/games/supported/LostLuggage.o \ src/games/supported/MontezumaRevenge.o \ + src/games/supported/MrDo.o \ src/games/supported/MsPacman.o \ src/games/supported/NameThisGame.o \ src/games/supported/Phoenix.o \ @@ -52,13 +63,17 @@ MODULE_OBJS := \ src/games/supported/RoadRunner.o \ src/games/supported/RoboTank.o \ src/games/supported/Seaquest.o \ + src/games/supported/SirLancelot.o \ src/games/supported/Skiing.o \ src/games/supported/Solaris.o \ src/games/supported/SpaceInvaders.o \ src/games/supported/StarGunner.o \ src/games/supported/Tennis.o \ + src/games/supported/Tetris.o \ src/games/supported/TimePilot.o \ + src/games/supported/Turmoil.o \ src/games/supported/Tutankham.o \ + src/games/supported/Trondead.o \ src/games/supported/UpNDown.o \ src/games/supported/Venture.o \ src/games/supported/VideoPinball.o \ diff --git a/atari_py/ale_interface/src/games/supported/Adventure.cpp b/atari_py/ale_interface/src/games/supported/Adventure.cpp new file mode 100644 index 0000000..58b124d --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Adventure.cpp @@ -0,0 +1,117 @@ +/* ***************************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "Adventure.hpp" + +#include "../RomUtils.hpp" + +AdventureSettings::AdventureSettings() { + + reset(); +} + +/* create a new instance of the rom */ +RomSettings* AdventureSettings::clone() const { + + RomSettings* rval = new AdventureSettings(); + *rval = *this; + return rval; +} + +/* process the latest information from ALE */ +void AdventureSettings::step(const System& system) { + + int chalice_status = readRam(&system, 0xB9); + bool chalice_in_yellow_castle = chalice_status == 0x12; + + if (chalice_in_yellow_castle) { + m_reward = 1; + } + + int player_status = readRam(&system, 0xE0); + bool player_eaten = player_status == 2; + + m_terminal = player_eaten || chalice_in_yellow_castle; +} + +/* is end of game */ +bool AdventureSettings::isTerminal() const { + + return m_terminal; +} + +/* get the most recently observed reward */ +reward_t AdventureSettings::getReward() const { + + return m_reward; +} + +/* is an action part of the minimal set? */ +bool AdventureSettings::isMinimal(const Action &a) const { + + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + case PLAYER_A_UPRIGHT: + case PLAYER_A_UPLEFT: + case PLAYER_A_DOWNRIGHT: + case PLAYER_A_DOWNLEFT: + case PLAYER_A_UPFIRE: + case PLAYER_A_RIGHTFIRE: + case PLAYER_A_LEFTFIRE: + case PLAYER_A_DOWNFIRE: + case PLAYER_A_UPRIGHTFIRE: + case PLAYER_A_UPLEFTFIRE: + case PLAYER_A_DOWNRIGHTFIRE: + case PLAYER_A_DOWNLEFTFIRE: + return true; + default: + return false; + } +} + +/* reset the state of the game */ +void AdventureSettings::reset() { + + m_reward = 0; + m_terminal = false; +} + +/* saves the state of the rom settings */ +void AdventureSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putBool(m_terminal); +} + +// loads the state of the rom settings +void AdventureSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_terminal = ser.getBool(); +} + diff --git a/atari_py/ale_interface/src/games/supported/Adventure.hpp b/atari_py/ale_interface/src/games/supported/Adventure.hpp new file mode 100644 index 0000000..faf4f65 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Adventure.hpp @@ -0,0 +1,75 @@ +/* ***************************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __ADVENTURE_HPP__ +#define __ADVENTURE_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for Adventure settings */ +class AdventureSettings : public RomSettings { + + public: + + AdventureSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + const char* rom() const { return "adventure"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + virtual int lives() { return 1; } + + private: + + bool m_terminal; + reward_t m_reward; + }; + +#endif // __ADVENTURE_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/AirRaid.cpp b/atari_py/ale_interface/src/games/supported/AirRaid.cpp index 3e7b251..d1a3524 100644 --- a/atari_py/ale_interface/src/games/supported/AirRaid.cpp +++ b/atari_py/ale_interface/src/games/supported/AirRaid.cpp @@ -101,3 +101,40 @@ ActionVect AirRaidSettings::getStartingActions() { startingActions.push_back(PLAYER_A_FIRE); return startingActions; } + + +// returns a list of mode that the game can be played in +ModeVect AirRaidSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i + 1; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void AirRaidSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0) { + m = 1; // the default mode is not valid in this game + } + if(m >= 1 && m <= getNumModes()) { + //open the mode selection panel + environment.pressSelect(20); + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xAA); + // press select until the correct mode is reached + while (mode != m) { + // hold select button for 10 frames + environment.pressSelect(10); + mode = readRam(&system, 0xAA); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } \ No newline at end of file diff --git a/atari_py/ale_interface/src/games/supported/AirRaid.hpp b/atari_py/ale_interface/src/games/supported/AirRaid.hpp index 4bd088f..8a847e6 100644 --- a/atari_py/ale_interface/src/games/supported/AirRaid.hpp +++ b/atari_py/ale_interface/src/games/supported/AirRaid.hpp @@ -34,6 +34,9 @@ class AirRaidSettings : public RomSettings { // the rom-name const char* rom() const { return "air_raid"; } + // get the available number of modes + unsigned int getNumModes() const { return 8; } + // create a new instance of the rom RomSettings* clone() const; @@ -49,7 +52,17 @@ class AirRaidSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - ActionVect getStartingActions(); + ActionVect getStartingActions(); + + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/Alien.cpp b/atari_py/ale_interface/src/games/supported/Alien.cpp index 239bb03..8cf4716 100644 --- a/atari_py/ale_interface/src/games/supported/Alien.cpp +++ b/atari_py/ale_interface/src/games/supported/Alien.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 65, 118, 134 and 142 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -142,3 +142,41 @@ void AlienSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect AlienSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void AlienSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x81); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(); + mode = readRam(&system, 0x81); + } + //update the number of lives + int byte = readRam(&system, 0xC0); + byte = byte & 15; + m_lives = byte; + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect AlienSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} diff --git a/atari_py/ale_interface/src/games/supported/Alien.hpp b/atari_py/ale_interface/src/games/supported/Alien.hpp index 5075fe0..7c3eb9b 100644 --- a/atari_py/ale_interface/src/games/supported/Alien.hpp +++ b/atari_py/ale_interface/src/games/supported/Alien.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 77 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class AlienSettings : public RomSettings { // the rom-name const char* rom() const { return "alien"; } + // get the available number of modes + unsigned int getNumModes() const { return 4; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class AlienSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return m_lives; } + virtual int lives() { return m_lives; } + + // list of modes that the game can be played in + // in this game, there are 4 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 4 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Amidar.cpp b/atari_py/ale_interface/src/games/supported/Amidar.cpp index 101e5ef..e8bf6e1 100644 --- a/atari_py/ale_interface/src/games/supported/Amidar.cpp +++ b/atari_py/ale_interface/src/games/supported/Amidar.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 46, 91, 100 and 108 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -123,3 +123,8 @@ void AmidarSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +DifficultyVect AmidarSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/Amidar.hpp b/atari_py/ale_interface/src/games/supported/Amidar.hpp index 7c3fa7a..03298a5 100644 --- a/atari_py/ale_interface/src/games/supported/Amidar.hpp +++ b/atari_py/ale_interface/src/games/supported/Amidar.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -64,7 +64,11 @@ class AmidarSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Assault.hpp b/atari_py/ale_interface/src/games/supported/Assault.hpp index 7c8bc9f..31033a6 100644 --- a/atari_py/ale_interface/src/games/supported/Assault.hpp +++ b/atari_py/ale_interface/src/games/supported/Assault.hpp @@ -64,7 +64,7 @@ class AssaultSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/Asterix.hpp b/atari_py/ale_interface/src/games/supported/Asterix.hpp index 7756d03..10d1f0e 100644 --- a/atari_py/ale_interface/src/games/supported/Asterix.hpp +++ b/atari_py/ale_interface/src/games/supported/Asterix.hpp @@ -60,14 +60,14 @@ class AsterixSettings : public RomSettings { // saves the state of the rom settings void saveState(Serializer & ser); - + // loads the state of the rom settings void loadState(Deserializer & ser); // Asterix requires the fire action to start the game ActionVect getStartingActions(); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/Asteroids.cpp b/atari_py/ale_interface/src/games/supported/Asteroids.cpp index 5fbab0d..26faa51 100644 --- a/atari_py/ale_interface/src/games/supported/Asteroids.cpp +++ b/atari_py/ale_interface/src/games/supported/Asteroids.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 42 - 44, 92, 102 and 110 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -48,13 +48,19 @@ RomSettings* AsteroidsSettings::clone() const { void AsteroidsSettings::step(const System& system) { // update the reward - reward_t score = getDecimalScore(62, 61, &system); + reward_t score = getDecimalScore(0xBE, 0xBD, &system); score *= 10; m_reward = score - m_score; + // Deal with score wrapping. In truth this should be done for all games and in a more + // uniform fashion. + if (m_reward < 0) { + const int WRAP_SCORE = 100000; + m_reward += WRAP_SCORE; + } m_score = score; // update terminal status - int byte = readRam(&system, 0x3C); + int byte = readRam(&system, 0xBC); m_lives = (byte - (byte & 15)) >> 4; m_terminal = (m_lives == 0); } @@ -125,3 +131,39 @@ void AsteroidsSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect AsteroidsSettings::getAvailableModes() { + ModeVect modes(getNumModes() - 1); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + modes.push_back(0x80); //this is the "kids" mode + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void AsteroidsSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < 32 || m == 0x80) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect AsteroidsSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/Asteroids.hpp b/atari_py/ale_interface/src/games/supported/Asteroids.hpp index 5834e24..01b5deb 100644 --- a/atari_py/ale_interface/src/games/supported/Asteroids.hpp +++ b/atari_py/ale_interface/src/games/supported/Asteroids.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class AsteroidsSettings : public RomSettings { // the rom-name const char* rom() const { return "asteroids"; } + // get the available number of modes + unsigned int getNumModes() const { return 33; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class AsteroidsSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 33 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Atlantis.cpp b/atari_py/ale_interface/src/games/supported/Atlantis.cpp index 080be66..aace2d2 100644 --- a/atari_py/ale_interface/src/games/supported/Atlantis.cpp +++ b/atari_py/ale_interface/src/games/supported/Atlantis.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 100, 110 and 118 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -128,3 +128,33 @@ void AtlantisSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect AtlantisSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void AtlantisSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x8D); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0x8D); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + diff --git a/atari_py/ale_interface/src/games/supported/Atlantis.hpp b/atari_py/ale_interface/src/games/supported/Atlantis.hpp index 74ea60f..20f0d73 100644 --- a/atari_py/ale_interface/src/games/supported/Atlantis.hpp +++ b/atari_py/ale_interface/src/games/supported/Atlantis.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class AtlantisSettings : public RomSettings { // the rom-name const char* rom() const { return "atlantis"; } + // get the available number of modes + unsigned int getNumModes() const { return 4; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class AtlantisSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/BankHeist.cpp b/atari_py/ale_interface/src/games/supported/BankHeist.cpp index 9960733..95cb7a0 100644 --- a/atari_py/ale_interface/src/games/supported/BankHeist.cpp +++ b/atari_py/ale_interface/src/games/supported/BankHeist.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 97, 107 and 115 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -130,3 +130,38 @@ void BankHeistSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect BankHeistSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i * 4; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void BankHeistSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m <= 28 && m % 4 == 0) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + // hold select button for 10 frames + environment.pressSelect(); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect BankHeistSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} diff --git a/atari_py/ale_interface/src/games/supported/BankHeist.hpp b/atari_py/ale_interface/src/games/supported/BankHeist.hpp index a9a6732..5441645 100644 --- a/atari_py/ale_interface/src/games/supported/BankHeist.hpp +++ b/atari_py/ale_interface/src/games/supported/BankHeist.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class BankHeistSettings : public RomSettings { // the rom-name const char* rom() const { return "bank_heist"; } + // get the available number of modes + unsigned int getNumModes() const { return 8; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class BankHeistSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/BattleZone.cpp b/atari_py/ale_interface/src/games/supported/BattleZone.cpp index 9295bef..35a15b2 100644 --- a/atari_py/ale_interface/src/games/supported/BattleZone.cpp +++ b/atari_py/ale_interface/src/games/supported/BattleZone.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 125, 135 and 143 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -143,3 +143,36 @@ void BattleZoneSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect BattleZoneSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i + 1; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void BattleZoneSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0) { + m = 1; // the default mode is not valid here + } + if(m >= 1 && m <= 3) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xA1); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0xA1); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + diff --git a/atari_py/ale_interface/src/games/supported/BattleZone.hpp b/atari_py/ale_interface/src/games/supported/BattleZone.hpp index ce69431..a884efe 100644 --- a/atari_py/ale_interface/src/games/supported/BattleZone.hpp +++ b/atari_py/ale_interface/src/games/supported/BattleZone.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class BattleZoneSettings : public RomSettings { // the rom-name const char* rom() const { return "battle_zone"; } + // get the available number of modes + unsigned int getNumModes() const { return 3; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class BattleZoneSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 3 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/BeamRider.cpp b/atari_py/ale_interface/src/games/supported/BeamRider.cpp index 5845d37..817fe92 100644 --- a/atari_py/ale_interface/src/games/supported/BeamRider.cpp +++ b/atari_py/ale_interface/src/games/supported/BeamRider.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 54 - 63, 113, 122 and 130 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -136,3 +136,8 @@ ActionVect BeamRiderSettings::getStartingActions() { return startingActions; } +DifficultyVect BeamRiderSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/BeamRider.hpp b/atari_py/ale_interface/src/games/supported/BeamRider.hpp index 99fa61c..b506ecb 100644 --- a/atari_py/ale_interface/src/games/supported/BeamRider.hpp +++ b/atari_py/ale_interface/src/games/supported/BeamRider.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 69 and 76 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -66,7 +66,11 @@ class BeamRiderSettings : public RomSettings { ActionVect getStartingActions(); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties. + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Berzerk.cpp b/atari_py/ale_interface/src/games/supported/Berzerk.cpp index e6926fb..a18c8a1 100644 --- a/atari_py/ale_interface/src/games/supported/Berzerk.cpp +++ b/atari_py/ale_interface/src/games/supported/Berzerk.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 44, 97, 107 and 115 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -130,3 +130,45 @@ void BerzerkSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect BerzerkSettings::getAvailableModes() { + ModeVect modes(getNumModes() - 3); + for (unsigned int i = 0; i < modes.size(); i++) { + //this is 1-12 in Atari-decimal (0x01 ... 0x09 0x10 0x11 0x12) + modes[i] = i + 1; + } + modes.push_back(0x10); + modes.push_back(0x11); + modes.push_back(0x12); + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void BerzerkSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0) { + m = 1; // The mode 0, which is the default, is not available in this game. + } + if(m >= 1 && (m <= 9 || m == 0x10 || m == 0x11 || m == 0x12)) { + // we wait that the game is ready to change mode + for(unsigned int i = 0; i < 20; i++) { + environment.act(PLAYER_A_NOOP, PLAYER_B_NOOP); + } + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + + diff --git a/atari_py/ale_interface/src/games/supported/Berzerk.hpp b/atari_py/ale_interface/src/games/supported/Berzerk.hpp index f0ff793..36824c2 100644 --- a/atari_py/ale_interface/src/games/supported/Berzerk.hpp +++ b/atari_py/ale_interface/src/games/supported/Berzerk.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class BerzerkSettings : public RomSettings { // the rom-name const char* rom() const { return "berzerk"; } + // get the available number of modes + unsigned int getNumModes() const { return 12; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,17 @@ class BerzerkSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 12 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + private: diff --git a/atari_py/ale_interface/src/games/supported/Bowling.cpp b/atari_py/ale_interface/src/games/supported/Bowling.cpp index d305243..cf39908 100644 --- a/atari_py/ale_interface/src/games/supported/Bowling.cpp +++ b/atari_py/ale_interface/src/games/supported/Bowling.cpp @@ -98,3 +98,35 @@ void BowlingSettings::loadState(Deserializer & ser) { m_terminal = ser.getBool(); } +// returns a list of mode that the game can be played in +ModeVect BowlingSettings::getAvailableModes() { + game_mode_t modes[] = {0, 2, 4}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void BowlingSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 2 || m == 4) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 2); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 2); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect BowlingSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/Bowling.hpp b/atari_py/ale_interface/src/games/supported/Bowling.hpp index 721f0ed..bae5f2e 100644 --- a/atari_py/ale_interface/src/games/supported/Bowling.hpp +++ b/atari_py/ale_interface/src/games/supported/Bowling.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 67 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class BowlingSettings : public RomSettings { // the rom-name const char* rom() const { return "bowling"; } + // get the available number of modes + unsigned int getNumModes() const { return 3; } + // create a new instance of the rom RomSettings* clone() const; @@ -65,7 +68,20 @@ class BowlingSettings : public RomSettings { void loadState(Deserializer & ser); // No lives in bowling! - virtual const int lives() { return 0; } + virtual int lives() { return 0; } + + // returns a list of mode that the game can be played in + // in this game, there are 3 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Boxing.cpp b/atari_py/ale_interface/src/games/supported/Boxing.cpp index 522709c..4d3e8bf 100644 --- a/atari_py/ale_interface/src/games/supported/Boxing.cpp +++ b/atari_py/ale_interface/src/games/supported/Boxing.cpp @@ -123,3 +123,7 @@ void BoxingSettings::loadState(Deserializer & ser) { m_terminal = ser.getBool(); } +DifficultyVect BoxingSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} diff --git a/atari_py/ale_interface/src/games/supported/Boxing.hpp b/atari_py/ale_interface/src/games/supported/Boxing.hpp index c0add39..6d5db32 100644 --- a/atari_py/ale_interface/src/games/supported/Boxing.hpp +++ b/atari_py/ale_interface/src/games/supported/Boxing.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 67 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -64,7 +64,11 @@ class BoxingSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 4 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Breakout.cpp b/atari_py/ale_interface/src/games/supported/Breakout.cpp index c7062c1..b6a93a8 100644 --- a/atari_py/ale_interface/src/games/supported/Breakout.cpp +++ b/atari_py/ale_interface/src/games/supported/Breakout.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 61, 101, 113 and 122 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -84,8 +84,6 @@ bool BreakoutSettings::isMinimal(const Action &a) const { case PLAYER_A_FIRE: case PLAYER_A_RIGHT: case PLAYER_A_LEFT: - case PLAYER_A_RIGHTFIRE: - case PLAYER_A_LEFTFIRE: return true; default: return false; @@ -122,3 +120,39 @@ void BreakoutSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect BreakoutSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i * 4; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void BreakoutSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes() * 4 && m % 4 == 0) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xB2); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(); + mode = readRam(&system, 0xB2); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + + +DifficultyVect BreakoutSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/Breakout.hpp b/atari_py/ale_interface/src/games/supported/Breakout.hpp index c229298..2ab3d9a 100644 --- a/atari_py/ale_interface/src/games/supported/Breakout.hpp +++ b/atari_py/ale_interface/src/games/supported/Breakout.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 68 and 76 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class BreakoutSettings : public RomSettings { // the rom-name const char* rom() const { return "breakout"; } + // get the available number of modes + unsigned int getNumModes() const { return 12; } + // create a new instance of the rom RomSettings* clone() const; @@ -65,7 +68,20 @@ class BreakoutSettings : public RomSettings { void loadState(Deserializer & ser); // remaining lives - const int lives() { return isTerminal() ? 0 : m_lives; } + int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Carnival.hpp b/atari_py/ale_interface/src/games/supported/Carnival.hpp index 6ee6fc2..b9662de 100644 --- a/atari_py/ale_interface/src/games/supported/Carnival.hpp +++ b/atari_py/ale_interface/src/games/supported/Carnival.hpp @@ -64,7 +64,7 @@ class CarnivalSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } private: diff --git a/atari_py/ale_interface/src/games/supported/Centipede.cpp b/atari_py/ale_interface/src/games/supported/Centipede.cpp index c3d628b..6f91974 100644 --- a/atari_py/ale_interface/src/games/supported/Centipede.cpp +++ b/atari_py/ale_interface/src/games/supported/Centipede.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 59, 116, 126 and 134 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -134,3 +134,32 @@ void CentipedeSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect CentipedeSettings::getAvailableModes() { + game_mode_t modes[] = {0x16, 0x56}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void CentipedeSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + if (m == 0) { + m = 0x16; // The default mode doesn't work here. + } + if(m == 0x16 || m == 0x56) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xA7); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0xA7); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + diff --git a/atari_py/ale_interface/src/games/supported/Centipede.hpp b/atari_py/ale_interface/src/games/supported/Centipede.hpp index e8ed11e..83ad1f5 100644 --- a/atari_py/ale_interface/src/games/supported/Centipede.hpp +++ b/atari_py/ale_interface/src/games/supported/Centipede.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class CentipedeSettings : public RomSettings { // the rom-name const char* rom() const { return "centipede"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class CentipedeSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/ChopperCommand.cpp b/atari_py/ale_interface/src/games/supported/ChopperCommand.cpp index 015eab3..1e03a6d 100644 --- a/atari_py/ale_interface/src/games/supported/ChopperCommand.cpp +++ b/atari_py/ale_interface/src/games/supported/ChopperCommand.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 42, 96, 106 and 114 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -56,13 +56,14 @@ void ChopperCommandSettings::step(const System& system) { // update terminal status m_lives = readRam(&system, 0xE4) & 0xF; m_terminal = (m_lives == 0); + m_is_started = (readRam(&system, 0xC2) == 1); } /* is end of game */ bool ChopperCommandSettings::isTerminal() const { - return m_terminal; + return (m_is_started && m_terminal); }; @@ -109,6 +110,7 @@ void ChopperCommandSettings::reset() { m_score = 0; m_terminal = false; m_lives = 3; + m_is_started = false; } @@ -129,3 +131,36 @@ void ChopperCommandSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } + +// returns a list of mode that the game can be played in +ModeVect ChopperCommandSettings::getAvailableModes() { + game_mode_t modes[] = {0, 2}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void ChopperCommandSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 2) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xE0); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0xE0); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect ChopperCommandSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/ChopperCommand.hpp b/atari_py/ale_interface/src/games/supported/ChopperCommand.hpp index b055aba..1989569 100644 --- a/atari_py/ale_interface/src/games/supported/ChopperCommand.hpp +++ b/atari_py/ale_interface/src/games/supported/ChopperCommand.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class ChopperCommandSettings : public RomSettings { // the rom-name const char* rom() const { return "chopper_command"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,10 +67,24 @@ class ChopperCommandSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return m_lives; } + virtual int lives() { return m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 2 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: + bool m_is_started; bool m_terminal; reward_t m_reward; reward_t m_score; diff --git a/atari_py/ale_interface/src/games/supported/CrazyClimber.cpp b/atari_py/ale_interface/src/games/supported/CrazyClimber.cpp index 825d816..8d7b879 100644 --- a/atari_py/ale_interface/src/games/supported/CrazyClimber.cpp +++ b/atari_py/ale_interface/src/games/supported/CrazyClimber.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 117, 126 and 124 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -124,3 +124,38 @@ void CrazyClimberSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect CrazyClimberSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void CrazyClimberSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect CrazyClimberSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/CrazyClimber.hpp b/atari_py/ale_interface/src/games/supported/CrazyClimber.hpp index 4f6aa26..5a00447 100644 --- a/atari_py/ale_interface/src/games/supported/CrazyClimber.hpp +++ b/atari_py/ale_interface/src/games/supported/CrazyClimber.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class CrazyClimberSettings : public RomSettings { // the rom-name const char* rom() const { return "crazy_climber"; } + // get the available number of modes + unsigned int getNumModes() const { return 4; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class CrazyClimberSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 4 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Defender.cpp b/atari_py/ale_interface/src/games/supported/Defender.cpp index 37c60c3..42b5a6e 100644 --- a/atari_py/ale_interface/src/games/supported/Defender.cpp +++ b/atari_py/ale_interface/src/games/supported/Defender.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 117, 126 and 134 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -134,3 +134,43 @@ void DefenderSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect DefenderSettings::getAvailableModes() { + ModeVect modes(getNumModes() - 1); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i + 1; + } + modes.push_back(16); //easy mode + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void DefenderSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0) { + m = 1; // The default mode (0) is not valid here. + } + if(m >= 1 && (m <= 9 || m == 16)) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x9B); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0x9B); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect DefenderSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + + diff --git a/atari_py/ale_interface/src/games/supported/Defender.hpp b/atari_py/ale_interface/src/games/supported/Defender.hpp index 35b20ff..7f21f4a 100644 --- a/atari_py/ale_interface/src/games/supported/Defender.hpp +++ b/atari_py/ale_interface/src/games/supported/Defender.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class DefenderSettings : public RomSettings { // the rom-name const char* rom() const { return "defender"; } + // get the available number of modes + unsigned int getNumModes() const { return 10; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class DefenderSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 10 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/DemonAttack.cpp b/atari_py/ale_interface/src/games/supported/DemonAttack.cpp index f985d8f..7f17016 100644 --- a/atari_py/ale_interface/src/games/supported/DemonAttack.cpp +++ b/atari_py/ale_interface/src/games/supported/DemonAttack.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 48, 89, 98 and 106 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -59,8 +59,10 @@ void DemonAttackSettings::step(const System& system) { // update terminal status int lives_displayed = readRam(&system, 0xF2); int display_flag = readRam(&system, 0xF1); - m_terminal = (lives_displayed == 0) && display_flag == 0xBD; + // for terminal checking, we must make sure that we do not detect incorrectly a level change as a game-over + m_terminal = (lives_displayed == 0) && display_flag == 0xBD && !m_level_change; m_lives = lives_displayed + 1; // Once we reach terminal, lives() will correctly return 0 + m_level_change = false; } @@ -102,6 +104,7 @@ void DemonAttackSettings::reset() { m_score = 0; m_terminal = false; m_lives = 4; + m_level_change = false; } @@ -121,3 +124,40 @@ void DemonAttackSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect DemonAttackSettings::getAvailableModes() { + game_mode_t modes[] = {1, 3, 5, 7}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void DemonAttackSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0) { + m = 1; // The default mode is not valid here + } + if(m == 1 || m == 3 || m == 5 || m == 7) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xEA); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(1); + mode = readRam(&system, 0xEA); + } + m_level_change = true; + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect DemonAttackSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + + diff --git a/atari_py/ale_interface/src/games/supported/DemonAttack.hpp b/atari_py/ale_interface/src/games/supported/DemonAttack.hpp index 444e4a2..3223522 100644 --- a/atari_py/ale_interface/src/games/supported/DemonAttack.hpp +++ b/atari_py/ale_interface/src/games/supported/DemonAttack.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class DemonAttackSettings : public RomSettings { // the rom-name const char* rom() const { return "demon_attack"; } + // get the available number of modes + unsigned int getNumModes() const { return 8; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class DemonAttackSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: @@ -72,6 +88,7 @@ class DemonAttackSettings : public RomSettings { reward_t m_reward; reward_t m_score; int m_lives; + bool m_level_change; }; #endif // __DEMONATTACK_HPP__ diff --git a/atari_py/ale_interface/src/games/supported/DonkeyKong.cpp b/atari_py/ale_interface/src/games/supported/DonkeyKong.cpp new file mode 100644 index 0000000..c3af6ec --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/DonkeyKong.cpp @@ -0,0 +1,109 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "DonkeyKong.hpp" + +#include "../RomUtils.hpp" + + +DonkeyKongSettings::DonkeyKongSettings() { + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* DonkeyKongSettings::clone() const { + RomSettings* rval = new DonkeyKongSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void DonkeyKongSettings::step(const System& system) { + // update the reward + int score = getDecimalScore(0x88, 0x87, &system); + score *= 100; + int reward = score - m_score; + m_reward = reward; + m_score = score; + + // update lives and terminal status + m_lives = readRam(&system, 0xA3); + m_terminal = (m_lives == 0) + && readRam(&system, 0x8F) == 0x03 + && readRam(&system, 0x8B) == 0x1F; +} + +/* is end of game */ +bool DonkeyKongSettings::isTerminal() const { + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t DonkeyKongSettings::getReward() const { + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool DonkeyKongSettings::isMinimal(const Action &a) const { + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + case PLAYER_A_UPRIGHT: + case PLAYER_A_UPLEFT: + case PLAYER_A_DOWNRIGHT: + case PLAYER_A_DOWNLEFT: + case PLAYER_A_UPFIRE: + case PLAYER_A_RIGHTFIRE: + case PLAYER_A_LEFTFIRE: + case PLAYER_A_DOWNFIRE: + case PLAYER_A_UPRIGHTFIRE: + case PLAYER_A_UPLEFTFIRE: + case PLAYER_A_DOWNRIGHTFIRE: + case PLAYER_A_DOWNLEFTFIRE: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void DonkeyKongSettings::reset() { + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 2; +} + +/* saves the state of the rom settings */ +void DonkeyKongSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void DonkeyKongSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + diff --git a/atari_py/ale_interface/src/games/supported/DonkeyKong.hpp b/atari_py/ale_interface/src/games/supported/DonkeyKong.hpp new file mode 100644 index 0000000..e485f41 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/DonkeyKong.hpp @@ -0,0 +1,63 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __DONKEYKONG_HPP__ +#define __DONKEYKONG_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for DonkeyKong */ +class DonkeyKongSettings : public RomSettings { + + public: + + DonkeyKongSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + // MD5 36b20c427975760cb9cf4a47e41369e4 + const char* rom() const { return "donkey_kong"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + private: + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __DONKEYKONG_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/DoubleDunk.cpp b/atari_py/ale_interface/src/games/supported/DoubleDunk.cpp index d9b5b2e..32deeee 100644 --- a/atari_py/ale_interface/src/games/supported/DoubleDunk.cpp +++ b/atari_py/ale_interface/src/games/supported/DoubleDunk.cpp @@ -116,3 +116,102 @@ ActionVect DoubleDunkSettings::getStartingActions() { startingActions.push_back(PLAYER_A_UPFIRE); return startingActions; } + +// returns a list of mode that the game can be played in +ModeVect DoubleDunkSettings::getAvailableModes() { + // this game has a menu that allows to define various yes/no options + // setting these options define in a way a different mode + // there are 4 relevant options, which makes 2^4=16 available modes + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +void DoubleDunkSettings::goDown(System &system, + StellaEnvironmentWrapper& environment) { + // this game has a menu that allows to define various yes/no options + // this function goes to the next option in the menu + unsigned int previousSelection = readRam(&system, 0xB0); + while(previousSelection == readRam(&system, 0xB0)){ + environment.act(PLAYER_A_DOWN, PLAYER_B_NOOP); + environment.act(PLAYER_A_NOOP, PLAYER_B_NOOP); + } +} + +void DoubleDunkSettings::activateOption(System &system, unsigned int bitOfInterest, + StellaEnvironmentWrapper& environment) { + // once we are at the proper option in the menu, + // if we want to enable it all we have to do is to go right + while((readRam(&system, 0x80) & bitOfInterest) != bitOfInterest) { + environment.act(PLAYER_A_RIGHT, PLAYER_B_NOOP); + environment.act(PLAYER_A_NOOP, PLAYER_B_NOOP); + } +} + +void DoubleDunkSettings::deactivateOption(System &system, unsigned int bitOfInterest, + StellaEnvironmentWrapper& environment) { + // once we are at the proper optio in the menu, + // if we want to disable it all we have to do is to go left + while((readRam(&system, 0x80) & bitOfInterest) == bitOfInterest) { + environment.act(PLAYER_A_LEFT, PLAYER_B_NOOP); + environment.act(PLAYER_A_NOOP, PLAYER_B_NOOP); + } +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void DoubleDunkSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + environment.pressSelect(); + + //discard the first two entries (irrelevant) + goDown(system, environment); + goDown(system, environment); + + //deal with the 3 points option + if(m & 1) { + activateOption(system, 0x08, environment); + } else { + deactivateOption(system, 0x08, environment); + } + + //deal with the 10 seconds option + goDown(system, environment); + if(m & 2) { + activateOption(system, 0x10, environment); + } else { + deactivateOption(system, 0x10, environment); + } + + //deal with the 3 seconds option + goDown(system, environment); + if(m & 4) { + activateOption(system, 0x04, environment); + } else { + deactivateOption(system, 0x04, environment); + } + + //deal with the foul option + goDown(system, environment); + if(m & 8) { + activateOption(system, 0x20, environment); + } else { + deactivateOption(system, 0x20, environment); + } + + //reset the environment to apply changes. + environment.softReset(); + //apply starting action + environment.act(PLAYER_A_UPFIRE, PLAYER_B_NOOP); + environment.act(PLAYER_A_NOOP, PLAYER_B_NOOP); + + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + diff --git a/atari_py/ale_interface/src/games/supported/DoubleDunk.hpp b/atari_py/ale_interface/src/games/supported/DoubleDunk.hpp index c568c1b..b884103 100644 --- a/atari_py/ale_interface/src/games/supported/DoubleDunk.hpp +++ b/atari_py/ale_interface/src/games/supported/DoubleDunk.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 69 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class DoubleDunkSettings : public RomSettings { // the rom-name const char* rom() const { return "double_dunk"; } + // get the available number of modes + unsigned int getNumModes() const { return 16; } + // create a new instance of the rom RomSettings* clone() const; @@ -66,13 +69,38 @@ class DoubleDunkSettings : public RomSettings { ActionVect getStartingActions(); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } + + // returns a list of mode that the game can be played in + // in this game, there are 16 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + private: bool m_terminal; reward_t m_reward; reward_t m_score; + + // this game has a menu that allows to define various yes/no options + // this function goes to the next option in the menu + void goDown(System &system, + StellaEnvironmentWrapper& environment); + + // once we are at the proper option in the menu, + // if we want to enable it all we have to do is to go right + void activateOption(System &system, unsigned int bitOfInterest, + StellaEnvironmentWrapper& environment); + + // once we are at the proper optio in the menu, + // if we want to disable it all we have to do is to go left + void deactivateOption(System &system, unsigned int bitOfInterest, + StellaEnvironmentWrapper& environment); }; #endif // __DOUBLEDUNK_HPP__ diff --git a/atari_py/ale_interface/src/games/supported/ElevatorAction.hpp b/atari_py/ale_interface/src/games/supported/ElevatorAction.hpp index 05c2a61..c9528ca 100644 --- a/atari_py/ale_interface/src/games/supported/ElevatorAction.hpp +++ b/atari_py/ale_interface/src/games/supported/ElevatorAction.hpp @@ -66,7 +66,7 @@ class ElevatorActionSettings : public RomSettings { ActionVect getStartingActions(); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/Enduro.hpp b/atari_py/ale_interface/src/games/supported/Enduro.hpp index 02420cf..dc1386e 100644 --- a/atari_py/ale_interface/src/games/supported/Enduro.hpp +++ b/atari_py/ale_interface/src/games/supported/Enduro.hpp @@ -66,7 +66,7 @@ class EnduroSettings : public RomSettings { ActionVect getStartingActions(); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } private: diff --git a/atari_py/ale_interface/src/games/supported/FishingDerby.cpp b/atari_py/ale_interface/src/games/supported/FishingDerby.cpp index af87c76..3bc0bba 100644 --- a/atari_py/ale_interface/src/games/supported/FishingDerby.cpp +++ b/atari_py/ale_interface/src/games/supported/FishingDerby.cpp @@ -12,7 +12,8 @@ #include "FishingDerby.hpp" #include "../RomUtils.hpp" -using namespace std; + +#include FishingDerbySettings::FishingDerbySettings() { @@ -34,14 +35,14 @@ RomSettings* FishingDerbySettings::clone() const { void FishingDerbySettings::step(const System& system) { // update the reward - int my_score = max(getDecimalScore(0xBD, &system), 0); - int oppt_score = max(getDecimalScore(0xBE, &system), 0); + int my_score = (std::max)(getDecimalScore(0xBD, &system), 0); + int oppt_score = (std::max)(getDecimalScore(0xBE, &system), 0); int score = my_score - oppt_score; m_reward = score - m_score; m_score = score; // update terminal status - int my_score_byte = readRam(&system, 0xBD); + int my_score_byte = readRam(&system, 0xBD); int my_oppt_score_byte = readRam(&system, 0xBE); m_terminal = my_score_byte == 0x99 || my_oppt_score_byte == 0x99; @@ -114,3 +115,9 @@ void FishingDerbySettings::loadState(Deserializer & ser) { m_terminal = ser.getBool(); } +DifficultyVect FishingDerbySettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + + diff --git a/atari_py/ale_interface/src/games/supported/FishingDerby.hpp b/atari_py/ale_interface/src/games/supported/FishingDerby.hpp index 1bcb439..2a79053 100644 --- a/atari_py/ale_interface/src/games/supported/FishingDerby.hpp +++ b/atari_py/ale_interface/src/games/supported/FishingDerby.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 67 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -64,7 +64,11 @@ class FishingDerbySettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 4 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Freeway.cpp b/atari_py/ale_interface/src/games/supported/Freeway.cpp index 3cce63b..f4a5ea4 100644 --- a/atari_py/ale_interface/src/games/supported/Freeway.cpp +++ b/atari_py/ale_interface/src/games/supported/Freeway.cpp @@ -1,4 +1,6 @@ /* ***************************************************************************** + * The method lives() is based on Xitari's code, from Google Inc. + * * A.L.E (Arcade Learning Environment) * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and * the Reinforcement Learning and Artificial Intelligence Laboratory @@ -16,7 +18,9 @@ FreewaySettings::FreewaySettings() { - reset(); + m_reward = 0; + m_score = 0; + m_terminal = false; } @@ -58,7 +62,6 @@ reward_t FreewaySettings::getReward() const { return m_reward; } - /* is an action part of the minimal set? */ bool FreewaySettings::isMinimal(const Action &a) const { @@ -95,3 +98,38 @@ void FreewaySettings::loadState(Deserializer & ser) { m_terminal = ser.getBool(); } +// returns a list of mode that the game can be played in +ModeVect FreewaySettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void FreewaySettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect FreewaySettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/Freeway.hpp b/atari_py/ale_interface/src/games/supported/Freeway.hpp index 6cf5bb0..83c72bd 100644 --- a/atari_py/ale_interface/src/games/supported/Freeway.hpp +++ b/atari_py/ale_interface/src/games/supported/Freeway.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 67 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -27,6 +27,7 @@ #ifndef __FREEWAY_HPP__ #define __FREEWAY_HPP__ +#include "stella_environment_wrapper.hpp" #include "../RomSettings.hpp" @@ -49,6 +50,9 @@ class FreewaySettings : public RomSettings { // the rom-name const char* rom() const { return "freeway"; } + // get the available number of modes + unsigned int getNumModes() const { return 8; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,10 +68,22 @@ class FreewaySettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } - private: + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); + + private: bool m_terminal; reward_t m_reward; reward_t m_score; diff --git a/atari_py/ale_interface/src/games/supported/Frogger.cpp b/atari_py/ale_interface/src/games/supported/Frogger.cpp new file mode 100644 index 0000000..6e8c449 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Frogger.cpp @@ -0,0 +1,96 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "Frogger.hpp" + +#include "../RomUtils.hpp" + +FroggerSettings::FroggerSettings() { + reset(); +} + +/* create a new instance of the rom */ +RomSettings* FroggerSettings::clone() const { + RomSettings* rval = new FroggerSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void FroggerSettings::step(const System& system) { + // update the reward + int score = getDecimalScore(0xCE, 0xCC, &system); + int reward = score - m_score; + m_reward = reward; + m_score = score; + + // update terminal status + m_lives = readRam(&system, 0xD0); + m_terminal = readRam(&system, 0xD0) == 0xFF; +} + + +/* is end of game */ +bool FroggerSettings::isTerminal() const { + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t FroggerSettings::getReward() const { + return m_reward; +} + +/* is an action part of the minimal set? */ +bool FroggerSettings::isMinimal(const Action &a) const { + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void FroggerSettings::reset() { + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 4; +} + +/* saves the state of the rom settings */ +void FroggerSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void FroggerSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + +ActionVect FroggerSettings::getStartingActions() { + ActionVect startingActions; + startingActions.push_back(RESET); + return startingActions; +} diff --git a/atari_py/ale_interface/src/games/supported/Frogger.hpp b/atari_py/ale_interface/src/games/supported/Frogger.hpp new file mode 100644 index 0000000..725bc36 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Frogger.hpp @@ -0,0 +1,64 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __FROGGER_HPP__ +#define __FROGGER_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for Frogger */ +class FroggerSettings : public RomSettings { + public: + FroggerSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + // MD5 081e2c114c9c20b61acf25fc95c71bf4 + const char* rom() const { return "frogger"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + // Frogger requires the RESET action to start the game + ActionVect getStartingActions(); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + private: + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __FROGGER_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/Frostbite.cpp b/atari_py/ale_interface/src/games/supported/Frostbite.cpp index 8cf0492..f089394 100644 --- a/atari_py/ale_interface/src/games/supported/Frostbite.cpp +++ b/atari_py/ale_interface/src/games/supported/Frostbite.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 61, 115, 123 and 131 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -133,3 +133,30 @@ void FrostbiteSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect FrostbiteSettings::getAvailableModes() { + game_mode_t modes[] = {0, 2}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void FrostbiteSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 2) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(1); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + diff --git a/atari_py/ale_interface/src/games/supported/Frostbite.hpp b/atari_py/ale_interface/src/games/supported/Frostbite.hpp index 0225e2a..c7720d2 100644 --- a/atari_py/ale_interface/src/games/supported/Frostbite.hpp +++ b/atari_py/ale_interface/src/games/supported/Frostbite.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class FrostbiteSettings : public RomSettings { // the rom-name const char* rom() const { return "frostbite"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,17 @@ class FrostbiteSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 2 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + private: diff --git a/atari_py/ale_interface/src/games/supported/Galaxian.cpp b/atari_py/ale_interface/src/games/supported/Galaxian.cpp new file mode 100644 index 0000000..9554e8b --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Galaxian.cpp @@ -0,0 +1,154 @@ +/* ***************************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "Galaxian.hpp" + +#include "../RomUtils.hpp" + +ActionVect GalaxianSettings::actions; + +GalaxianSettings::GalaxianSettings() { + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* GalaxianSettings::clone() const { + RomSettings* rval = new GalaxianSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void GalaxianSettings::step(const System& system) { + // update the reward + int score = getDecimalScore(0xAE, 0xAD, 0xAC, &system); + // reward cannot get negative in this game. When it does, it means that the score has looped + // (overflow) + m_reward = score - m_score; + if(m_reward < 0) { + // 1000000 is the highest possible score + const int maximumScore = 1000000; + m_reward = (maximumScore - m_score) + score; + } + m_score = score; + + // update terminal and lives + // If bit 0x80 is on, then game is over + int some_byte = readRam(&system, 0xBF); + m_terminal = (some_byte & 0x80); + if (m_terminal) { + // Force lives to zero when the game is over since otherwise it would be left as 1 + m_lives = 0; + } else { + m_lives = readRam(&system, 0xB9) + 1; // 0xB9 keeps the number of lives shown below the screen + } +} + + +/* is end of game */ +bool GalaxianSettings::isTerminal() const { + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t GalaxianSettings::getReward() const { + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool GalaxianSettings::isMinimal(const Action &a) const { + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_LEFT: + case PLAYER_A_RIGHT: + case PLAYER_A_FIRE: + case PLAYER_A_LEFTFIRE: + case PLAYER_A_RIGHTFIRE: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void GalaxianSettings::reset() { + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 3; +} + + +/* saves the state of the rom settings */ +void GalaxianSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void GalaxianSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + +// returns a list of mode that the game can be played in +ModeVect GalaxianSettings::getAvailableModes() { + game_mode_t modes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void GalaxianSettings::setMode(game_mode_t mode, System &system, + StellaEnvironmentWrapper& environment) { + + if (mode == 0) + mode = 1; + + if (mode >= 1 && mode <= 9) { + // press select until the correct mode is reached + while (mode != static_cast(readRam(&system, 0xB3))) { + environment.pressSelect(); + } + //reset the environment to apply changes. + environment.softReset(); + } else + { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } +} + + + + diff --git a/atari_py/ale_interface/src/games/supported/Galaxian.hpp b/atari_py/ale_interface/src/games/supported/Galaxian.hpp new file mode 100644 index 0000000..9e69a0d --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Galaxian.hpp @@ -0,0 +1,99 @@ +/* ***************************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __GALAXIAN_HPP__ +#define __GALAXIAN_HPP__ + +#include "../RomSettings.hpp" + + +// RL wrapper for Galaxian +class GalaxianSettings : public RomSettings { + + public: + + GalaxianSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + const char* rom() const { return "galaxian"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // get the available number of modes + unsigned int getNumModes() const { return 9; } + + // returns a list of mode that the game can be played in + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t mode, System &system, + StellaEnvironmentWrapper& environment); + + // Returns a list of difficulties that the game can be played in. + // 2 difficulties: 0 is left B, 1 is left A + DifficultyVect getAvailableDifficulties() + { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); + } + + + private: + + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; + + static ActionVect actions; +}; + +#endif // __GALAXIAN_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/Gopher.cpp b/atari_py/ale_interface/src/games/supported/Gopher.cpp index 85e1f72..d3c3ac5 100644 --- a/atari_py/ale_interface/src/games/supported/Gopher.cpp +++ b/atari_py/ale_interface/src/games/supported/Gopher.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 61, 62, 105, 114 and 122 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -127,3 +127,36 @@ ActionVect GopherSettings::getStartingActions() { startingActions.push_back(PLAYER_A_FIRE); return startingActions; } + +// returns a list of mode that the game can be played in +ModeVect GopherSettings::getAvailableModes() { + game_mode_t modes[] = {0, 2}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void GopherSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 2) { + environment.softReset(); + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xD3); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(5); + mode = readRam(&system, 0xD3); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect GopherSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} diff --git a/atari_py/ale_interface/src/games/supported/Gopher.hpp b/atari_py/ale_interface/src/games/supported/Gopher.hpp index 4fc443c..6381a37 100644 --- a/atari_py/ale_interface/src/games/supported/Gopher.hpp +++ b/atari_py/ale_interface/src/games/supported/Gopher.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 70 and 77 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class GopherSettings : public RomSettings { // the rom-name const char* rom() const { return "gopher"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -67,7 +70,20 @@ class GopherSettings : public RomSettings { // Gopher requires the fire action to start the game ActionVect getStartingActions(); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Gravitar.cpp b/atari_py/ale_interface/src/games/supported/Gravitar.cpp index 4b3f783..87da77a 100644 --- a/atari_py/ale_interface/src/games/supported/Gravitar.cpp +++ b/atari_py/ale_interface/src/games/supported/Gravitar.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 63, 116, 125 and 133 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -139,3 +139,51 @@ ActionVect GravitarSettings::getStartingActions() { startingActions.push_back(PLAYER_A_FIRE); return startingActions; } + +// returns a list of mode that the game can be played in +ModeVect GravitarSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void GravitarSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + // hold select button for 10 frames + environment.pressSelect(10); + mode = readRam(&system, 0x80); + } + + //update the number of lives + switch(m){ + case 0: + case 2: + m_lives = 6; + break; + case 1: + m_lives = 15; + break; + case 3: + m_lives = 100; + break; + case 4: + m_lives = 25; + break; + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } diff --git a/atari_py/ale_interface/src/games/supported/Gravitar.hpp b/atari_py/ale_interface/src/games/supported/Gravitar.hpp index 503f57c..6788040 100644 --- a/atari_py/ale_interface/src/games/supported/Gravitar.hpp +++ b/atari_py/ale_interface/src/games/supported/Gravitar.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 70 and 77 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class GravitarSettings : public RomSettings { // the rom-name const char* rom() const { return "gravitar"; } + // get the available number of modes + unsigned int getNumModes() const { return 5; } + // create a new instance of the rom RomSettings* clone() const; @@ -67,7 +70,16 @@ class GravitarSettings : public RomSettings { // Gravitar requires the fire action to start the game ActionVect getStartingActions(); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 5 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/Hero.cpp b/atari_py/ale_interface/src/games/supported/Hero.cpp index 1da2216..90fe023 100644 --- a/atari_py/ale_interface/src/games/supported/Hero.cpp +++ b/atari_py/ale_interface/src/games/supported/Hero.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 96, 106 and 114 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -129,3 +129,32 @@ void HeroSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect HeroSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void HeroSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } diff --git a/atari_py/ale_interface/src/games/supported/Hero.hpp b/atari_py/ale_interface/src/games/supported/Hero.hpp index d4a0ef8..67cf00c 100644 --- a/atari_py/ale_interface/src/games/supported/Hero.hpp +++ b/atari_py/ale_interface/src/games/supported/Hero.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class HeroSettings : public RomSettings { // the rom-name const char* rom() const { return "hero"; } + // get the available number of modes + unsigned int getNumModes() const { return 5; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class HeroSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/IceHockey.cpp b/atari_py/ale_interface/src/games/supported/IceHockey.cpp index 3cc29bd..0111cae 100644 --- a/atari_py/ale_interface/src/games/supported/IceHockey.cpp +++ b/atari_py/ale_interface/src/games/supported/IceHockey.cpp @@ -12,7 +12,8 @@ #include "IceHockey.hpp" #include "../RomUtils.hpp" -using namespace std; + +#include IceHockeySettings::IceHockeySettings() { @@ -34,10 +35,10 @@ RomSettings* IceHockeySettings::clone() const { void IceHockeySettings::step(const System& system) { // update the reward - int my_score = max(getDecimalScore(0x8A, &system), 0); - int oppt_score = max(getDecimalScore(0x8B, &system), 0); + int my_score = (std::max)(getDecimalScore(0x8A, &system), 0); + int oppt_score = (std::max)(getDecimalScore(0x8B, &system), 0); int score = my_score - oppt_score; - int reward = min(score - m_score, 1); + int reward = (std::min)(score - m_score, 1); m_reward = reward; m_score = score; @@ -115,3 +116,35 @@ void IceHockeySettings::loadState(Deserializer & ser) { m_terminal = ser.getBool(); } +// returns a list of mode that the game can be played in +ModeVect IceHockeySettings::getAvailableModes() { + game_mode_t modes[] = {0, 2}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void IceHockeySettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 2) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect IceHockeySettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/IceHockey.hpp b/atari_py/ale_interface/src/games/supported/IceHockey.hpp index 2e2a68f..c76a501 100644 --- a/atari_py/ale_interface/src/games/supported/IceHockey.hpp +++ b/atari_py/ale_interface/src/games/supported/IceHockey.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 67 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class IceHockeySettings : public RomSettings { // the rom-name const char* rom() const { return "ice_hockey"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class IceHockeySettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } + + // returns a list of mode that the game can be played in + // in this game, there are 2 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 4 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/JamesBond.cpp b/atari_py/ale_interface/src/games/supported/JamesBond.cpp index 2974aa7..5873665 100644 --- a/atari_py/ale_interface/src/games/supported/JamesBond.cpp +++ b/atari_py/ale_interface/src/games/supported/JamesBond.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 63, 64, 117, 126 and 134 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -134,3 +134,34 @@ void JamesBondSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } + +// returns a list of mode that the game can be played in +ModeVect JamesBondSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void JamesBondSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 1) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x8C); + // press select until the correct mode is reached + // in the welcome screen, the value of the mode is increased by 0x48 + while (mode != m && mode != m + 0x48) { + environment.pressSelect(20); + mode = readRam(&system, 0x8C); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } diff --git a/atari_py/ale_interface/src/games/supported/JamesBond.hpp b/atari_py/ale_interface/src/games/supported/JamesBond.hpp index 7544ca7..785331c 100644 --- a/atari_py/ale_interface/src/games/supported/JamesBond.hpp +++ b/atari_py/ale_interface/src/games/supported/JamesBond.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class JamesBondSettings : public RomSettings { // the rom-name const char* rom() const { return "jamesbond"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class JamesBondSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 2 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/JourneyEscape.cpp b/atari_py/ale_interface/src/games/supported/JourneyEscape.cpp index 2b72710..d754c18 100644 --- a/atari_py/ale_interface/src/games/supported/JourneyEscape.cpp +++ b/atari_py/ale_interface/src/games/supported/JourneyEscape.cpp @@ -1,4 +1,6 @@ /* ***************************************************************************** + * The method lives() is based on Xitari's code, from Google Inc. + * * A.L.E (Arcade Learning Environment) * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and * the Reinforcement Learning and Artificial Intelligence Laboratory @@ -116,4 +118,8 @@ ActionVect JourneyEscapeSettings::getStartingActions() { return startingActions; } +DifficultyVect JourneyEscapeSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} diff --git a/atari_py/ale_interface/src/games/supported/JourneyEscape.hpp b/atari_py/ale_interface/src/games/supported/JourneyEscape.hpp index 25220e2..088d39f 100644 --- a/atari_py/ale_interface/src/games/supported/JourneyEscape.hpp +++ b/atari_py/ale_interface/src/games/supported/JourneyEscape.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 70 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -67,7 +67,11 @@ class JourneyEscapeSettings : public RomSettings { // Journey Escape requires the fire action to start the game ActionVect getStartingActions(); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Kaboom.cpp b/atari_py/ale_interface/src/games/supported/Kaboom.cpp new file mode 100644 index 0000000..890f4ba --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Kaboom.cpp @@ -0,0 +1,99 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "Kaboom.hpp" + +#include "../RomUtils.hpp" +#include + +KaboomSettings::KaboomSettings() { + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* KaboomSettings::clone() const { + + RomSettings* rval = new KaboomSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void KaboomSettings::step(const System& system) { + + // update the reward + reward_t score = getDecimalScore(0xA5, 0xA4, 0xA3, &system); + m_reward = score - m_score; + m_score = score; + + // update terminal status + int lives = readRam(&system, 0xA1); + m_terminal = lives == 0x0 || m_score == 999999; +} + + +/* is end of game */ +bool KaboomSettings::isTerminal() const { + + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t KaboomSettings::getReward() const { + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool KaboomSettings::isMinimal(const Action &a) const { + + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void KaboomSettings::reset() { + + m_reward = 0; + m_score = 0; + m_terminal = false; +} + +/* saves the state of the rom settings */ +void KaboomSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); +} + +// loads the state of the rom settings +void KaboomSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); +} + +ActionVect KaboomSettings::getStartingActions() { + ActionVect startingActions; + startingActions.push_back(PLAYER_A_FIRE); + return startingActions; +} diff --git a/atari_py/ale_interface/src/games/supported/Kaboom.hpp b/atari_py/ale_interface/src/games/supported/Kaboom.hpp new file mode 100644 index 0000000..fd6e54b --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Kaboom.hpp @@ -0,0 +1,65 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __KABOOM_HPP__ +#define __KABOOM_HPP__ + +#include "../RomSettings.hpp" + +/* RL wrapper for Kaboom settings */ +class KaboomSettings: public RomSettings +{ + +public: + + KaboomSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + const char* rom() const + { + return "kaboom"; + } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + ActionVect getStartingActions(); + +private: + + bool m_terminal; + reward_t m_reward; + reward_t m_score; +}; + +#endif // __KABOOM__ + diff --git a/atari_py/ale_interface/src/games/supported/Kangaroo.cpp b/atari_py/ale_interface/src/games/supported/Kangaroo.cpp index aca14a3..d3e1b1e 100644 --- a/atari_py/ale_interface/src/games/supported/Kangaroo.cpp +++ b/atari_py/ale_interface/src/games/supported/Kangaroo.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 60, 113, 123 and 131 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -48,7 +48,7 @@ RomSettings* KangarooSettings::clone() const { void KangarooSettings::step(const System& system) { // update the reward - int score = getDecimalScore(40, 39, &system); + int score = getDecimalScore(0xA8, 0xA7, &system); score *= 100; int reward = score - m_score; m_reward = reward; @@ -131,3 +131,31 @@ void KangarooSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect KangarooSettings::getAvailableModes() { + game_mode_t modes[] = {0, 1}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void KangarooSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if( m == 0 || m == 1){ + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xBA); + // press select until the correct mode is reached + //in the welcome screen, the value of the mode is increased by 0x80 + while(mode != m && mode != m + 0x80) { + environment.pressSelect(2); + mode = readRam(&system, 0xBA); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + diff --git a/atari_py/ale_interface/src/games/supported/Kangaroo.hpp b/atari_py/ale_interface/src/games/supported/Kangaroo.hpp index 9ee79b2..c94fec6 100644 --- a/atari_py/ale_interface/src/games/supported/Kangaroo.hpp +++ b/atari_py/ale_interface/src/games/supported/Kangaroo.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class KangarooSettings : public RomSettings { // the rom-name const char* rom() const { return "kangaroo"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class KangarooSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/KeystoneKapers.cpp b/atari_py/ale_interface/src/games/supported/KeystoneKapers.cpp new file mode 100644 index 0000000..b5119fe --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/KeystoneKapers.cpp @@ -0,0 +1,108 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "KeystoneKapers.hpp" + +#include "../RomUtils.hpp" + + +KeystoneKapersSettings::KeystoneKapersSettings() { + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* KeystoneKapersSettings::clone() const { + RomSettings* rval = new KeystoneKapersSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void KeystoneKapersSettings::step(const System& system) { + // update the reward + int score = getDecimalScore(0x9C, 0x9B, &system); + int reward = score - m_score; + m_reward = reward; + m_score = score; + + m_lives = readRam(&system, 0x96); + m_terminal = (m_lives == 0) && readRam(&system, 0x88) == 0x00; +} + + +/* is end of game */ +bool KeystoneKapersSettings::isTerminal() const { + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t KeystoneKapersSettings::getReward() const { + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool KeystoneKapersSettings::isMinimal(const Action &a) const { + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + case PLAYER_A_UPRIGHT: + case PLAYER_A_UPLEFT: + case PLAYER_A_DOWNRIGHT: + case PLAYER_A_DOWNLEFT: + case PLAYER_A_UPFIRE: + case PLAYER_A_RIGHTFIRE: + case PLAYER_A_LEFTFIRE: + case PLAYER_A_DOWNFIRE: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void KeystoneKapersSettings::reset() { + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 3; +} + +/* saves the state of the rom settings */ +void KeystoneKapersSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void KeystoneKapersSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + +ActionVect KeystoneKapersSettings::getStartingActions() { + ActionVect startingActions; + startingActions.push_back(RESET); + return startingActions; +} + diff --git a/atari_py/ale_interface/src/games/supported/KeystoneKapers.hpp b/atari_py/ale_interface/src/games/supported/KeystoneKapers.hpp new file mode 100644 index 0000000..4385ba4 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/KeystoneKapers.hpp @@ -0,0 +1,65 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __KEYSTONEKAPERS_HPP__ +#define __KEYSTONEKAPERS_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for KeystoneKapers */ +class KeystoneKapersSettings : public RomSettings { + + public: + KeystoneKapersSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + // MD5 be929419902e21bd7830a7a7d746195d + const char* rom() const { return "keystone_kapers"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + // Keystone Kapers requires the reset button to start the game + ActionVect getStartingActions(); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + private: + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __KEYSTONEKAPERS_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/Kingkong.cpp b/atari_py/ale_interface/src/games/supported/Kingkong.cpp new file mode 100644 index 0000000..30e0b01 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Kingkong.cpp @@ -0,0 +1,113 @@ +/* ***************************************************************************** + * The lines 61, 102, 110 and 118 are based on Xitari's code, from Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "Kingkong.hpp" + +#include "../RomUtils.hpp" + +KingkongSettings::KingkongSettings() { + reset(); +} + +/* create a new instance of the rom */ +RomSettings* KingkongSettings::clone() const { + RomSettings* rval = new KingkongSettings(); + *rval = *this; + return rval; +} + +/* process the latest information from ALE */ +void KingkongSettings::step(const System& system) { + // update the reward + int score = getDecimalScore(0x83, 0x82, &system); + int reward = score - m_score; + m_reward = reward; + m_score = score; + + // update terminal status + m_lives = readRam(&system, 0xEE); + m_terminal = (m_lives == 0); +} + +/* is end of game */ +bool KingkongSettings::isTerminal() const { + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t KingkongSettings::getReward() const { + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool KingkongSettings::isMinimal(const Action &a) const { + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void KingkongSettings::reset() { + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 3; +} + +/* saves the state of the rom settings */ +void KingkongSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void KingkongSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + +ActionVect KingkongSettings::getStartingActions() { + ActionVect startingActions; + startingActions.push_back(RESET); + return startingActions; +} + + diff --git a/atari_py/ale_interface/src/games/supported/Kingkong.hpp b/atari_py/ale_interface/src/games/supported/Kingkong.hpp new file mode 100644 index 0000000..8462b5f --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Kingkong.hpp @@ -0,0 +1,77 @@ +/* ***************************************************************************** + * The lines 70 and 77 are based on Xitari's code, from Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __KINGKONG_HPP__ +#define __KINGKONG_HPP__ + +#include "../RomSettings.hpp" + +/* RL wrapper for Kingkong */ +class KingkongSettings : public RomSettings { + public: + KingkongSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + const char* rom() const { return "king_kong"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + // Kingkong requires the fire action to start the game + ActionVect getStartingActions(); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + private: + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __KINGKONG_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/Koolaid.cpp b/atari_py/ale_interface/src/games/supported/Koolaid.cpp new file mode 100644 index 0000000..9b118c0 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Koolaid.cpp @@ -0,0 +1,101 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "Koolaid.hpp" + +#include "../RomUtils.hpp" + + +KoolaidSettings::KoolaidSettings() { + + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* KoolaidSettings::clone() const { + + RomSettings* rval = new KoolaidSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void KoolaidSettings::step(const System& system) { + + // update the reward + int score = getDecimalScore(0x81, 0x80, &system); + score *= 100; + int reward = score - m_score; + m_reward = reward; + m_score = score; + + m_terminal = readRam(&system, 0xD1) == 0x80; +} + + +/* is end of game */ +bool KoolaidSettings::isTerminal() const { + + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t KoolaidSettings::getReward() const { + + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool KoolaidSettings::isMinimal(const Action &a) const { + + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + case PLAYER_A_UPRIGHT: + case PLAYER_A_UPLEFT: + case PLAYER_A_DOWNRIGHT: + case PLAYER_A_DOWNLEFT: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void KoolaidSettings::reset() { + + m_reward = 0; + m_score = 0; + m_terminal = false; +} + +/* saves the state of the rom settings */ +void KoolaidSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); +} + +// loads the state of the rom settings +void KoolaidSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); +} + diff --git a/atari_py/ale_interface/src/games/supported/Koolaid.hpp b/atari_py/ale_interface/src/games/supported/Koolaid.hpp new file mode 100644 index 0000000..f6377a1 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Koolaid.hpp @@ -0,0 +1,63 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __KOOLAID_HPP__ +#define __KOOLAID_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for Koolaid */ +class KoolaidSettings : public RomSettings { + + public: + + KoolaidSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + const char* rom() const { return "koolaid"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + virtual int lives() { return 0; } + + private: + + bool m_terminal; + reward_t m_reward; + reward_t m_score; +}; + +#endif // __KOOLAID_HPP__ + + diff --git a/atari_py/ale_interface/src/games/supported/Krull.hpp b/atari_py/ale_interface/src/games/supported/Krull.hpp index 12da53a..7af6a4c 100644 --- a/atari_py/ale_interface/src/games/supported/Krull.hpp +++ b/atari_py/ale_interface/src/games/supported/Krull.hpp @@ -64,7 +64,7 @@ class KrullSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/KungFuMaster.hpp b/atari_py/ale_interface/src/games/supported/KungFuMaster.hpp index e6f7bdf..e8c42ea 100644 --- a/atari_py/ale_interface/src/games/supported/KungFuMaster.hpp +++ b/atari_py/ale_interface/src/games/supported/KungFuMaster.hpp @@ -64,7 +64,7 @@ class KungFuMasterSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/LaserGates.cpp b/atari_py/ale_interface/src/games/supported/LaserGates.cpp new file mode 100644 index 0000000..bcbaf8c --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/LaserGates.cpp @@ -0,0 +1,109 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "LaserGates.hpp" + +#include "../RomUtils.hpp" + +LaserGatesSettings::LaserGatesSettings() { + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* LaserGatesSettings::clone() const { + RomSettings* rval = new LaserGatesSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void LaserGatesSettings::step(const System& system) { + // update the reward + int score = getDecimalScore(0x82, 0x81, 0x80, &system); + int reward = score - m_score; + m_reward = reward; + m_score = score; + + // update terminal status + m_terminal = readRam(&system, 0x83) == 0x00; +} + + +/* is end of game */ +bool LaserGatesSettings::isTerminal() const { + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t LaserGatesSettings::getReward() const { + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool LaserGatesSettings::isMinimal(const Action &a) const { + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + case PLAYER_A_UPRIGHT: + case PLAYER_A_UPLEFT: + case PLAYER_A_DOWNRIGHT: + case PLAYER_A_DOWNLEFT: + case PLAYER_A_UPFIRE: + case PLAYER_A_RIGHTFIRE: + case PLAYER_A_LEFTFIRE: + case PLAYER_A_DOWNFIRE: + case PLAYER_A_UPRIGHTFIRE: + case PLAYER_A_UPLEFTFIRE: + case PLAYER_A_DOWNRIGHTFIRE: + case PLAYER_A_DOWNLEFTFIRE: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void LaserGatesSettings::reset() { + m_reward = 0; + m_score = 0; + m_terminal = false; +} + +/* saves the state of the rom settings */ +void LaserGatesSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); +} + +// loads the state of the rom settings +void LaserGatesSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); +} + +ActionVect LaserGatesSettings::getStartingActions() { + ActionVect startingActions; + startingActions.push_back(RESET); + return startingActions; +} + + diff --git a/atari_py/ale_interface/src/games/supported/LaserGates.hpp b/atari_py/ale_interface/src/games/supported/LaserGates.hpp new file mode 100644 index 0000000..b597342 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/LaserGates.hpp @@ -0,0 +1,63 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __LASERGATES_HPP__ +#define __LASERGATES_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for Laser Gates */ +class LaserGatesSettings : public RomSettings { + public: + LaserGatesSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + // MD5 1fa58679d4a39052bd9db059e8cda4ad + const char* rom() const { return "laser_gates"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + // LaserGates requires the fire action to start the game + ActionVect getStartingActions(); + + virtual int lives() { return 0; } + + private: + bool m_terminal; + reward_t m_reward; + reward_t m_score; +}; + +#endif // __LASERGATES_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/LostLuggage.cpp b/atari_py/ale_interface/src/games/supported/LostLuggage.cpp new file mode 100644 index 0000000..bcff928 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/LostLuggage.cpp @@ -0,0 +1,120 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "LostLuggage.hpp" + +#include "../RomUtils.hpp" + +LostLuggageSettings::LostLuggageSettings() { + reset(); +} + +/* create a new instance of the rom */ +RomSettings* LostLuggageSettings::clone() const { + RomSettings* rval = new LostLuggageSettings(); + *rval = *this; + return rval; +} + +/* process the latest information from ALE */ +void LostLuggageSettings::step(const System& system) { + // update the reward + int score = getDecimalScore(0x96, 0x95, 0x94, &system); + int reward = score - m_score; + m_reward = reward; + m_score = score; + + // update terminal status + m_lives = readRam(&system, 0xCA); + m_terminal = (m_lives == 0) + && readRam(&system, 0xC8) == 0x0A + && readRam(&system, 0xA5) == 0x00 + && readRam(&system, 0xA9) == 0x00; +} + +/* is end of game */ +bool LostLuggageSettings::isTerminal() const { + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t LostLuggageSettings::getReward() const { + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool LostLuggageSettings::isMinimal(const Action &a) const { + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + case PLAYER_A_UPRIGHT: + case PLAYER_A_UPLEFT: + case PLAYER_A_DOWNRIGHT: + case PLAYER_A_DOWNLEFT: + return true; + default: + return false; + } +} + +bool LostLuggageSettings::isLegal(const Action &a) const { + switch (a) { + // Don't allow pressing 'fire' + case PLAYER_A_FIRE: + case PLAYER_A_UPFIRE: + case PLAYER_A_DOWNFIRE: + case PLAYER_A_LEFTFIRE: + case PLAYER_A_RIGHTFIRE: + case PLAYER_A_UPLEFTFIRE: + case PLAYER_A_UPRIGHTFIRE: + case PLAYER_A_DOWNLEFTFIRE: + case PLAYER_A_DOWNRIGHTFIRE: + return false; + default: + return true; + } +} + +/* reset the state of the game */ +void LostLuggageSettings::reset() { + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 3; +} + +/* saves the state of the rom settings */ +void LostLuggageSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void LostLuggageSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + +ActionVect LostLuggageSettings::getStartingActions() { + ActionVect startingActions; + startingActions.push_back(RESET); + return startingActions; +} + diff --git a/atari_py/ale_interface/src/games/supported/LostLuggage.hpp b/atari_py/ale_interface/src/games/supported/LostLuggage.hpp new file mode 100644 index 0000000..f1ed845 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/LostLuggage.hpp @@ -0,0 +1,66 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __LOSTLUGGAGE_HPP__ +#define __LOSTLUGGAGE_HPP__ + +#include "../RomSettings.hpp" + +/* RL wrapper for Lost Luggage */ +class LostLuggageSettings : public RomSettings { + public: + LostLuggageSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + // MD5 7c00e7a205d3fda98eb20da7c9c50a55 + const char* rom() const { return "lost_luggage"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // FIRE resets the game, prevent this + bool isLegal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + // LostLuggage requires the fire action to start the game + ActionVect getStartingActions(); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + private: + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __LOSTLUGGAGE_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/MontezumaRevenge.hpp b/atari_py/ale_interface/src/games/supported/MontezumaRevenge.hpp index 4c54224..7c7c271 100644 --- a/atari_py/ale_interface/src/games/supported/MontezumaRevenge.hpp +++ b/atari_py/ale_interface/src/games/supported/MontezumaRevenge.hpp @@ -64,7 +64,7 @@ class MontezumaRevengeSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/MrDo.cpp b/atari_py/ale_interface/src/games/supported/MrDo.cpp new file mode 100644 index 0000000..5fc32e4 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/MrDo.cpp @@ -0,0 +1,111 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "MrDo.hpp" + +#include "../RomUtils.hpp" + + +MrDoSettings::MrDoSettings() { + + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* MrDoSettings::clone() const { + + RomSettings* rval = new MrDoSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void MrDoSettings::step(const System& system) { + + // update the reward + int score = getDecimalScore(0x82, 0x83, &system); + score *= 10; + int reward = score - m_score; + m_reward = reward; + m_score = score; + + // update terminal status + m_lives = readRam(&system, 0xDB); + m_terminal = readRam(&system, 0xDA) == 0x40; +} + + +/* is end of game */ +bool MrDoSettings::isTerminal() const { + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t MrDoSettings::getReward() const { + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool MrDoSettings::isMinimal(const Action &a) const { + + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + case PLAYER_A_UPFIRE: + case PLAYER_A_RIGHTFIRE: + case PLAYER_A_LEFTFIRE: + case PLAYER_A_DOWNFIRE: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void MrDoSettings::reset() { + + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 4; +} + +/* saves the state of the rom settings */ +void MrDoSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void MrDoSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + +ActionVect MrDoSettings::getStartingActions() { + ActionVect startingActions; + startingActions.push_back(PLAYER_A_FIRE); + return startingActions; +} + diff --git a/atari_py/ale_interface/src/games/supported/MrDo.hpp b/atari_py/ale_interface/src/games/supported/MrDo.hpp new file mode 100644 index 0000000..01614b8 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/MrDo.hpp @@ -0,0 +1,66 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __MRDO_HPP__ +#define __MRDO_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for MrDo */ +class MrDoSettings : public RomSettings { + + public: + + MrDoSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + const char* rom() const { return "mr_do"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + // Mr. Do requires the fire action to start the game + ActionVect getStartingActions(); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + private: + + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __MRDO_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/MsPacman.cpp b/atari_py/ale_interface/src/games/supported/MsPacman.cpp index 2ae0456..df04ea8 100644 --- a/atari_py/ale_interface/src/games/supported/MsPacman.cpp +++ b/atari_py/ale_interface/src/games/supported/MsPacman.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 62, 106, 116 and 124 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -124,3 +124,50 @@ void MsPacmanSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect MsPacmanSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void MsPacmanSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + if(m == 0) { //this is the standard variation of the game + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x99); + // read the variation + unsigned char var = readRam(&system, 0xA1); + // press select until the correct mode is reached + while(mode != 1 || var != 1) { + // hold select button for 10 frames + environment.pressSelect(10); + mode = readRam(&system, 0x99); + var = readRam(&system, 0xA1); + } + } else { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x99); + // read the variation + unsigned char var = readRam(&system, 0xA1); + // press select until the correct mode is reached + while(mode != m || var != 0) { + // hold select button for 10 frames + environment.pressSelect(10); + mode = readRam(&system, 0x99); + var = readRam(&system, 0xA1); + } + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } diff --git a/atari_py/ale_interface/src/games/supported/MsPacman.hpp b/atari_py/ale_interface/src/games/supported/MsPacman.hpp index 655c873..d19956e 100644 --- a/atari_py/ale_interface/src/games/supported/MsPacman.hpp +++ b/atari_py/ale_interface/src/games/supported/MsPacman.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class MsPacmanSettings : public RomSettings { // the rom-name const char* rom() const { return "ms_pacman"; } + // get the available number of modes + unsigned int getNumModes() const { return 4; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class MsPacmanSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 8 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/NameThisGame.cpp b/atari_py/ale_interface/src/games/supported/NameThisGame.cpp index 0da879c..da9083f 100644 --- a/atari_py/ale_interface/src/games/supported/NameThisGame.cpp +++ b/atari_py/ale_interface/src/games/supported/NameThisGame.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 57, 99, 107 and 115 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -115,3 +115,38 @@ void NameThisGameSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect NameThisGameSettings::getAvailableModes() { + game_mode_t modes[] = {0x08, 0x18, 0x28}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void NameThisGameSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0) { + m = 0x08; // the default mode is not valid here + } + if(m == 0x08 || m == 0x18 || m == 0x28) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xDE); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0xDE); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect NameThisGameSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/NameThisGame.hpp b/atari_py/ale_interface/src/games/supported/NameThisGame.hpp index d5f2a1c..727e3e3 100644 --- a/atari_py/ale_interface/src/games/supported/NameThisGame.hpp +++ b/atari_py/ale_interface/src/games/supported/NameThisGame.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class NameThisGameSettings : public RomSettings { // the rom-name const char* rom() const { return "name_this_game"; } + // get the available number of modes + unsigned int getNumModes() const { return 3; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class NameThisGameSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 3 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Phoenix.hpp b/atari_py/ale_interface/src/games/supported/Phoenix.hpp index 0253040..a5abc3a 100644 --- a/atari_py/ale_interface/src/games/supported/Phoenix.hpp +++ b/atari_py/ale_interface/src/games/supported/Phoenix.hpp @@ -64,7 +64,7 @@ class PhoenixSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/Pitfall.hpp b/atari_py/ale_interface/src/games/supported/Pitfall.hpp index 4b21131..8e5e88d 100644 --- a/atari_py/ale_interface/src/games/supported/Pitfall.hpp +++ b/atari_py/ale_interface/src/games/supported/Pitfall.hpp @@ -66,7 +66,7 @@ class PitfallSettings : public RomSettings { ActionVect getStartingActions(); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/Pong.cpp b/atari_py/ale_interface/src/games/supported/Pong.cpp index 3518cd4..4d5efbf 100644 --- a/atari_py/ale_interface/src/games/supported/Pong.cpp +++ b/atari_py/ale_interface/src/games/supported/Pong.cpp @@ -99,3 +99,38 @@ void PongSettings::loadState(Deserializer & ser) { m_terminal = ser.getBool(); } +// returns a list of mode that the game can be played in +ModeVect PongSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void PongSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x96); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0x96); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect PongSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/Pong.hpp b/atari_py/ale_interface/src/games/supported/Pong.hpp index 94c9e97..ccd85f7 100644 --- a/atari_py/ale_interface/src/games/supported/Pong.hpp +++ b/atari_py/ale_interface/src/games/supported/Pong.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 67 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class PongSettings : public RomSettings { // the rom-name const char* rom() const { return "pong"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class PongSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); + + // returns a list of mode that the game can be played in + // in this game, there are 2 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/Pooyan.cpp b/atari_py/ale_interface/src/games/supported/Pooyan.cpp index 39ce834..15ff1df 100644 --- a/atari_py/ale_interface/src/games/supported/Pooyan.cpp +++ b/atari_py/ale_interface/src/games/supported/Pooyan.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 61, 102, 110 and 118 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -118,3 +118,35 @@ void PooyanSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect PooyanSettings::getAvailableModes() { + game_mode_t modes[] = {0x0A, 0x1E, 0x32, 0x46}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void PooyanSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + + if (m == 0) { + m = 0x0A; // The default mode (0) is not valid here. + } + if(m == 0x0A || m == 0x1E || m == 0x32 || m == 0x46) { + environment.pressSelect(2); + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xBD); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0xBD); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + diff --git a/atari_py/ale_interface/src/games/supported/Pooyan.hpp b/atari_py/ale_interface/src/games/supported/Pooyan.hpp index 6a192af..14d4d26 100644 --- a/atari_py/ale_interface/src/games/supported/Pooyan.hpp +++ b/atari_py/ale_interface/src/games/supported/Pooyan.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class PooyanSettings : public RomSettings { // the rom-name const char* rom() const { return "pooyan"; } + // get the available number of modes + unsigned int getNumModes() const { return 4; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class PooyanSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 4 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/PrivateEye.cpp b/atari_py/ale_interface/src/games/supported/PrivateEye.cpp index e38ab8c..b9d271e 100644 --- a/atari_py/ale_interface/src/games/supported/PrivateEye.cpp +++ b/atari_py/ale_interface/src/games/supported/PrivateEye.cpp @@ -115,3 +115,40 @@ ActionVect PrivateEyeSettings::getStartingActions() { startingActions.push_back(PLAYER_A_UP); return startingActions; } + +// returns a list of mode that the game can be played in +ModeVect PrivateEyeSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void PrivateEyeSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + + +DifficultyVect PrivateEyeSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/PrivateEye.hpp b/atari_py/ale_interface/src/games/supported/PrivateEye.hpp index 5af2c5c..ece77b9 100644 --- a/atari_py/ale_interface/src/games/supported/PrivateEye.hpp +++ b/atari_py/ale_interface/src/games/supported/PrivateEye.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 69 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class PrivateEyeSettings : public RomSettings { // the rom-name const char* rom() const { return "private_eye"; } + // get the available number of modes + unsigned int getNumModes() const { return 5; } + // create a new instance of the rom RomSettings* clone() const; @@ -66,7 +69,20 @@ class PrivateEyeSettings : public RomSettings { ActionVect getStartingActions(); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } + + // returns a list of mode that the game can be played in + // in this game, there are 5 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 4 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/QBert.cpp b/atari_py/ale_interface/src/games/supported/QBert.cpp index f144f41..fd460a3 100644 --- a/atari_py/ale_interface/src/games/supported/QBert.cpp +++ b/atari_py/ale_interface/src/games/supported/QBert.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 57, 59, 115, 124 and 133 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -57,7 +57,7 @@ void QBertSettings::step(const System& system) { int livesAsChar = static_cast(lives_value); if (m_last_lives - 1 == livesAsChar) m_lives--; - m_last_lives = lives_value; + m_last_lives = livesAsChar; // update the reward // Ignore reward if reset the game via the fire button; otherwise the agent @@ -133,3 +133,8 @@ void QBertSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +DifficultyVect QBertSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/QBert.hpp b/atari_py/ale_interface/src/games/supported/QBert.hpp index ccf1244..cdb98cc 100644 --- a/atari_py/ale_interface/src/games/supported/QBert.hpp +++ b/atari_py/ale_interface/src/games/supported/QBert.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 75 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -64,7 +64,11 @@ class QBertSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/RiverRaid.cpp b/atari_py/ale_interface/src/games/supported/RiverRaid.cpp index 872f421..10b09c8 100644 --- a/atari_py/ale_interface/src/games/supported/RiverRaid.cpp +++ b/atari_py/ale_interface/src/games/supported/RiverRaid.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 84 - 91 and 143 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -159,3 +159,9 @@ void RiverRaidSettings::loadState(Deserializer & ser) { m_terminal = ser.getBool(); m_lives_byte = ser.getInt(); } + +DifficultyVect RiverRaidSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/RiverRaid.hpp b/atari_py/ale_interface/src/games/supported/RiverRaid.hpp index 4586971..812aa31 100644 --- a/atari_py/ale_interface/src/games/supported/RiverRaid.hpp +++ b/atari_py/ale_interface/src/games/supported/RiverRaid.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 68, 73 and 79 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -65,7 +65,11 @@ class RiverRaidSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : numericLives(); } + virtual int lives() { return isTerminal() ? 0 : numericLives(); } + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/RoadRunner.hpp b/atari_py/ale_interface/src/games/supported/RoadRunner.hpp index af576ed..d9fe7a0 100644 --- a/atari_py/ale_interface/src/games/supported/RoadRunner.hpp +++ b/atari_py/ale_interface/src/games/supported/RoadRunner.hpp @@ -64,7 +64,7 @@ class RoadRunnerSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/RoboTank.hpp b/atari_py/ale_interface/src/games/supported/RoboTank.hpp index af93525..3d800f3 100644 --- a/atari_py/ale_interface/src/games/supported/RoboTank.hpp +++ b/atari_py/ale_interface/src/games/supported/RoboTank.hpp @@ -64,7 +64,7 @@ class RoboTankSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/Seaquest.cpp b/atari_py/ale_interface/src/games/supported/Seaquest.cpp index 01a6915..8495796 100644 --- a/atari_py/ale_interface/src/games/supported/Seaquest.cpp +++ b/atari_py/ale_interface/src/games/supported/Seaquest.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 57, 110, 119 and 127 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -127,3 +127,7 @@ void SeaquestSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +DifficultyVect SeaquestSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} diff --git a/atari_py/ale_interface/src/games/supported/Seaquest.hpp b/atari_py/ale_interface/src/games/supported/Seaquest.hpp index 145c4f6..4d1a9cf 100644 --- a/atari_py/ale_interface/src/games/supported/Seaquest.hpp +++ b/atari_py/ale_interface/src/games/supported/Seaquest.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -64,7 +64,11 @@ class SeaquestSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/SirLancelot.cpp b/atari_py/ale_interface/src/games/supported/SirLancelot.cpp new file mode 100644 index 0000000..a2d1580 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/SirLancelot.cpp @@ -0,0 +1,103 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "SirLancelot.hpp" + +#include "../RomUtils.hpp" + + +SirLancelotSettings::SirLancelotSettings() { + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* SirLancelotSettings::clone() const { + RomSettings* rval = new SirLancelotSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void SirLancelotSettings::step(const System& system) { + // update the reward + int score = getDecimalScore(0xA0, 0x9F, 0x9E, &system); + int reward = score - m_score; + m_reward = reward; + m_score = score; + + // update terminal status + m_lives = readRam(&system, 0xA9); + m_terminal = (m_lives == 0) && readRam(&system, 0xA7) == 0xA0; +} + + +/* is end of game */ +bool SirLancelotSettings::isTerminal() const { + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t SirLancelotSettings::getReward() const { + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool SirLancelotSettings::isMinimal(const Action &a) const { + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_RIGHTFIRE: + case PLAYER_A_LEFTFIRE: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void SirLancelotSettings::reset() { + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 3; +} + +/* saves the state of the rom settings */ +void SirLancelotSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void SirLancelotSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + +ActionVect SirLancelotSettings::getStartingActions() { + ActionVect startingActions; + startingActions.push_back(RESET); + startingActions.push_back(PLAYER_A_LEFT); + return startingActions; +} + + diff --git a/atari_py/ale_interface/src/games/supported/SirLancelot.hpp b/atari_py/ale_interface/src/games/supported/SirLancelot.hpp new file mode 100644 index 0000000..6eb5784 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/SirLancelot.hpp @@ -0,0 +1,64 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __SIRLANCELOT_HPP__ +#define __SIRLANCELOT_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for Up N Down */ +class SirLancelotSettings : public RomSettings { + public: + SirLancelotSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + // MD5 7ead257e8b5a44cac538f5f54c7a0023 + const char* rom() const { return "sir_lancelot"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + // SirLancelot requires the reset+left action to start the game + ActionVect getStartingActions(); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + private: + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __SIRLANCELOT_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/Skiing.hpp b/atari_py/ale_interface/src/games/supported/Skiing.hpp index 8dde08e..e4c3688 100644 --- a/atari_py/ale_interface/src/games/supported/Skiing.hpp +++ b/atari_py/ale_interface/src/games/supported/Skiing.hpp @@ -68,7 +68,7 @@ class SkiingSettings : public RomSettings { ActionVect getStartingActions(); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } private: diff --git a/atari_py/ale_interface/src/games/supported/Solaris.hpp b/atari_py/ale_interface/src/games/supported/Solaris.hpp index 3cb098e..732a111 100644 --- a/atari_py/ale_interface/src/games/supported/Solaris.hpp +++ b/atari_py/ale_interface/src/games/supported/Solaris.hpp @@ -64,7 +64,7 @@ class SolarisSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } private: diff --git a/atari_py/ale_interface/src/games/supported/SpaceInvaders.cpp b/atari_py/ale_interface/src/games/supported/SpaceInvaders.cpp index c3297c5..606b82f 100644 --- a/atari_py/ale_interface/src/games/supported/SpaceInvaders.cpp +++ b/atari_py/ale_interface/src/games/supported/SpaceInvaders.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 54 - 59, 100, 109 and 117 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -124,3 +124,37 @@ void SpaceInvadersSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect SpaceInvadersSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void SpaceInvadersSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xDC); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0xDC); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect SpaceInvadersSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} \ No newline at end of file diff --git a/atari_py/ale_interface/src/games/supported/SpaceInvaders.hpp b/atari_py/ale_interface/src/games/supported/SpaceInvaders.hpp index a8d2b96..7fc96a9 100644 --- a/atari_py/ale_interface/src/games/supported/SpaceInvaders.hpp +++ b/atari_py/ale_interface/src/games/supported/SpaceInvaders.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class SpaceInvadersSettings : public RomSettings { // the rom-name const char* rom() const { return "space_invaders"; } + // get the available number of modes + unsigned int getNumModes() const { return 16; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,21 @@ class SpaceInvadersSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 16 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); + private: diff --git a/atari_py/ale_interface/src/games/supported/StarGunner.cpp b/atari_py/ale_interface/src/games/supported/StarGunner.cpp index 4bfb134..4946fcf 100644 --- a/atari_py/ale_interface/src/games/supported/StarGunner.cpp +++ b/atari_py/ale_interface/src/games/supported/StarGunner.cpp @@ -1,6 +1,5 @@ /* ***************************************************************************** - * The lines 71, 73, 126, 127, 135, 136, 144 and 145 are based on Xitari's code, - * from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -146,3 +145,32 @@ void StarGunnerSettings::loadState(Deserializer & ser) { m_game_started = ser.getBool(); } +// returns a list of mode that the game can be played in +ModeVect StarGunnerSettings::getAvailableModes() { + ModeVect modes(getNumModes()); + for (unsigned int i = 0; i < modes.size(); i++) { + modes[i] = i; + } + return modes; +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void StarGunnerSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m < getNumModes()) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xF4); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(15); + mode = readRam(&system, 0xF4); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } diff --git a/atari_py/ale_interface/src/games/supported/StarGunner.hpp b/atari_py/ale_interface/src/games/supported/StarGunner.hpp index d7cf46b..e971e1d 100644 --- a/atari_py/ale_interface/src/games/supported/StarGunner.hpp +++ b/atari_py/ale_interface/src/games/supported/StarGunner.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67, 74 and 75 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class StarGunnerSettings : public RomSettings { // the rom-name const char* rom() const { return "star_gunner"; } + // get the available number of modes + unsigned int getNumModes() const { return 4; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class StarGunnerSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 4 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/Tennis.cpp b/atari_py/ale_interface/src/games/supported/Tennis.cpp index e27cf9f..2fdad35 100644 --- a/atari_py/ale_interface/src/games/supported/Tennis.cpp +++ b/atari_py/ale_interface/src/games/supported/Tennis.cpp @@ -129,3 +129,35 @@ void TennisSettings::loadState(Deserializer & ser) { m_prev_delta_score = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect TennisSettings::getAvailableModes() { + game_mode_t modes[] = {0, 2}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void TennisSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 2) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x80); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0x80); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect TennisSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/Tennis.hpp b/atari_py/ale_interface/src/games/supported/Tennis.hpp index b234213..eef9f9f 100644 --- a/atari_py/ale_interface/src/games/supported/Tennis.hpp +++ b/atari_py/ale_interface/src/games/supported/Tennis.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The line 67 is based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class TennisSettings : public RomSettings { // the rom-name const char* rom() const { return "tennis"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class TennisSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return 0; } + virtual int lives() { return 0; } + + // returns a list of mode that the game can be played in + // in this game, there are 2 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 4 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Tetris.cpp b/atari_py/ale_interface/src/games/supported/Tetris.cpp new file mode 100644 index 0000000..6d8371a --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Tetris.cpp @@ -0,0 +1,130 @@ +/* ***************************************************************************** + * ALE support for Tetris https://github.com/udibr/tetris26 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "Tetris.hpp" + +#include "../RomUtils.hpp" + + +TetrisSettings::TetrisSettings() { + + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* TetrisSettings::clone() const { + + RomSettings* rval = new TetrisSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void TetrisSettings::step(const System& system) { + + // update the reward + reward_t score = getDecimalScore(0x71, 0x72, &system); + if (score > m_score) { + m_reward = score - m_score; + } else { + m_reward = 0; + } + m_score = score; + + if (!m_started) { + m_started = true; + } + + int byte_val = readRam(&system, 0x73); + m_terminal = m_started && (byte_val & 0x80); + if (m_terminal) { + m_score = 0; + m_started = false; + } +} + + +/* is end of game */ +bool TetrisSettings::isTerminal() const { + + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t TetrisSettings::getReward() const { + + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool TetrisSettings::isMinimal(const Action &a) const { + + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void TetrisSettings::reset() { + + m_reward = 0; + m_score = 0; + m_lives = 0; + m_terminal = false; + m_started = false; +} + + +/* saves the state of the rom settings */ +void TetrisSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putBool(m_started); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void TetrisSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_started = ser.getBool(); + m_lives = ser.getInt(); +} + diff --git a/atari_py/ale_interface/src/games/supported/Tetris.hpp b/atari_py/ale_interface/src/games/supported/Tetris.hpp new file mode 100644 index 0000000..509291b --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Tetris.hpp @@ -0,0 +1,78 @@ +/* ***************************************************************************** + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __TETRIS_HPP__ +#define __TETRIS_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for Tetris */ +class TetrisSettings : public RomSettings { + + public: + + TetrisSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + const char* rom() const { return "tetris"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + // remaining lives + int lives() { return isTerminal() ? 0 : m_lives; } + + private: + + bool m_terminal; + bool m_started; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __TETRIS_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/TimePilot.cpp b/atari_py/ale_interface/src/games/supported/TimePilot.cpp index bafc9ce..4aa8ebd 100644 --- a/atari_py/ale_interface/src/games/supported/TimePilot.cpp +++ b/atari_py/ale_interface/src/games/supported/TimePilot.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 64, 109, 117 and 125 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -125,3 +125,8 @@ void TimePilotSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } + +DifficultyVect TimePilotSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} diff --git a/atari_py/ale_interface/src/games/supported/TimePilot.hpp b/atari_py/ale_interface/src/games/supported/TimePilot.hpp index a9dfec5..f4735a3 100644 --- a/atari_py/ale_interface/src/games/supported/TimePilot.hpp +++ b/atari_py/ale_interface/src/games/supported/TimePilot.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -64,7 +64,11 @@ class TimePilotSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 3 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Trondead.cpp b/atari_py/ale_interface/src/games/supported/Trondead.cpp new file mode 100644 index 0000000..4ca5da7 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Trondead.cpp @@ -0,0 +1,115 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "Trondead.hpp" + +#include "../RomUtils.hpp" + + +TrondeadSettings::TrondeadSettings() { + + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* TrondeadSettings::clone() const { + + RomSettings* rval = new TrondeadSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void TrondeadSettings::step(const System& system) { + + // update the reward + int score = getDecimalScore(0xBF, 0xBE, 0xBD, &system); + int reward = score - m_score; + m_reward = reward; + m_score = score; + + // update terminal status + int hit_count = readRam(&system, 0xC8); + m_terminal = (hit_count == 5); // Five times hit + m_lives = 5 - hit_count; +} + + +/* is end of game */ +bool TrondeadSettings::isTerminal() const { + + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t TrondeadSettings::getReward() const { + + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool TrondeadSettings::isMinimal(const Action &a) const { + + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + case PLAYER_A_UPRIGHT: + case PLAYER_A_UPLEFT: + case PLAYER_A_DOWNRIGHT: + case PLAYER_A_DOWNLEFT: + case PLAYER_A_UPFIRE: + case PLAYER_A_RIGHTFIRE: + case PLAYER_A_LEFTFIRE: + case PLAYER_A_DOWNFIRE: + case PLAYER_A_UPRIGHTFIRE: + case PLAYER_A_UPLEFTFIRE: + case PLAYER_A_DOWNRIGHTFIRE: + case PLAYER_A_DOWNLEFTFIRE: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void TrondeadSettings::reset() { + + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 5; +} + +/* saves the state of the rom settings */ +void TrondeadSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void TrondeadSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + diff --git a/atari_py/ale_interface/src/games/supported/Trondead.hpp b/atari_py/ale_interface/src/games/supported/Trondead.hpp new file mode 100644 index 0000000..722f618 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Trondead.hpp @@ -0,0 +1,63 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __TRONDEAD_HPP__ +#define __TRONDEAD_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for Trondead */ +class TrondeadSettings : public RomSettings { + + public: + + TrondeadSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + const char* rom() const { return "trondead"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + private: + + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __TRONDEAD_HPP__ + diff --git a/atari_py/ale_interface/src/games/supported/Turmoil.cpp b/atari_py/ale_interface/src/games/supported/Turmoil.cpp new file mode 100644 index 0000000..f763865 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Turmoil.cpp @@ -0,0 +1,118 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#include "Turmoil.hpp" + +#include "../RomUtils.hpp" + + +TurmoilSettings::TurmoilSettings() { + + reset(); +} + + +/* create a new instance of the rom */ +RomSettings* TurmoilSettings::clone() const { + + RomSettings* rval = new TurmoilSettings(); + *rval = *this; + return rval; +} + + +/* process the latest information from ALE */ +void TurmoilSettings::step(const System& system) { + + // update the reward + int score = getDecimalScore(0x89, 0x8A, &system); + score += readRam(&system, 0xD3); + score *= 10; + int reward = score - m_score; + m_reward = reward; + m_score = score; + + // update terminal status + int lives_byte = readRam(&system, 0xB9); + m_terminal = (lives_byte == 0) && readRam(&system, 0xC5) == 0x01; + + m_lives = lives_byte; +} + + +/* is end of game */ +bool TurmoilSettings::isTerminal() const { + + return m_terminal; +}; + + +/* get the most recently observed reward */ +reward_t TurmoilSettings::getReward() const { + + return m_reward; +} + + +/* is an action part of the minimal set? */ +bool TurmoilSettings::isMinimal(const Action &a) const { + + switch (a) { + case PLAYER_A_NOOP: + case PLAYER_A_FIRE: + case PLAYER_A_UP: + case PLAYER_A_RIGHT: + case PLAYER_A_LEFT: + case PLAYER_A_DOWN: + case PLAYER_A_UPRIGHT: + case PLAYER_A_UPLEFT: + case PLAYER_A_DOWNRIGHT: + case PLAYER_A_DOWNLEFT: + case PLAYER_A_RIGHTFIRE: + case PLAYER_A_LEFTFIRE: + return true; + default: + return false; + } +} + + +/* reset the state of the game */ +void TurmoilSettings::reset() { + + m_reward = 0; + m_score = 0; + m_terminal = false; + m_lives = 4; +} + +/* saves the state of the rom settings */ +void TurmoilSettings::saveState(Serializer & ser) { + ser.putInt(m_reward); + ser.putInt(m_score); + ser.putBool(m_terminal); + ser.putInt(m_lives); +} + +// loads the state of the rom settings +void TurmoilSettings::loadState(Deserializer & ser) { + m_reward = ser.getInt(); + m_score = ser.getInt(); + m_terminal = ser.getBool(); + m_lives = ser.getInt(); +} + +ActionVect TurmoilSettings::getStartingActions() { + ActionVect startingActions; + startingActions.push_back(PLAYER_A_FIRE); + return startingActions; +} + diff --git a/atari_py/ale_interface/src/games/supported/Turmoil.hpp b/atari_py/ale_interface/src/games/supported/Turmoil.hpp new file mode 100644 index 0000000..004b21e --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Turmoil.hpp @@ -0,0 +1,69 @@ +/* ***************************************************************************** + * A.L.E (Arcade Learning Environment) + * Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and + * the Reinforcement Learning and Artificial Intelligence Laboratory + * Released under the GNU General Public License; see License.txt for details. + * + * Based on: Stella -- "An Atari 2600 VCS Emulator" + * Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team + * + * ***************************************************************************** + */ +#ifndef __TURMOIL_HPP__ +#define __TURMOIL_HPP__ + +#include "../RomSettings.hpp" + + +/* RL wrapper for Turmoil */ +class TurmoilSettings : public RomSettings { + + public: + + TurmoilSettings(); + + // reset + void reset(); + + // is end of game + bool isTerminal() const; + + // get the most recently observed reward + reward_t getReward() const; + + // the rom-name + // MD5 sum of ROM file: + // 7a5463545dfb2dcfdafa6074b2f2c15e Turmoil.bin + const char* rom() const { return "turmoil"; } + + // create a new instance of the rom + RomSettings* clone() const; + + // is an action part of the minimal set? + bool isMinimal(const Action& a) const; + + // process the latest information from ALE + void step(const System& system); + + // saves the state of the rom settings + void saveState(Serializer & ser); + + // loads the state of the rom settings + void loadState(Deserializer & ser); + + // Turmoil requires the fire action to start the game + ActionVect getStartingActions(); + + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + private: + + bool m_terminal; + reward_t m_reward; + reward_t m_score; + int m_lives; +}; + +#endif // __TURMOIL_HPP__ + + diff --git a/atari_py/ale_interface/src/games/supported/Tutankham.cpp b/atari_py/ale_interface/src/games/supported/Tutankham.cpp index a87b639..2ad9e0c 100644 --- a/atari_py/ale_interface/src/games/supported/Tutankham.cpp +++ b/atari_py/ale_interface/src/games/supported/Tutankham.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 63, 106, 114 and 122 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -122,3 +122,29 @@ void TutankhamSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect TutankhamSettings::getAvailableModes() { + game_mode_t modes[] = {0, 4, 8, 12}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void TutankhamSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 4 || m == 8 || m == 12) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xAB); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0xAB); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } diff --git a/atari_py/ale_interface/src/games/supported/Tutankham.hpp b/atari_py/ale_interface/src/games/supported/Tutankham.hpp index 67b215a..39eedbe 100644 --- a/atari_py/ale_interface/src/games/supported/Tutankham.hpp +++ b/atari_py/ale_interface/src/games/supported/Tutankham.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class TutankhamSettings : public RomSettings { // the rom-name const char* rom() const { return "tutankham"; } + // get the available number of modes + unsigned int getNumModes() const { return 4; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,16 @@ class TutankhamSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal()? 0 : m_lives; } + virtual int lives() { return isTerminal()? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 4 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); private: diff --git a/atari_py/ale_interface/src/games/supported/UpNDown.cpp b/atari_py/ale_interface/src/games/supported/UpNDown.cpp index 57944f8..14fd706 100644 --- a/atari_py/ale_interface/src/games/supported/UpNDown.cpp +++ b/atari_py/ale_interface/src/games/supported/UpNDown.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 61, 102, 110 and 118 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -124,4 +124,8 @@ ActionVect UpNDownSettings::getStartingActions() { return startingActions; } +DifficultyVect UpNDownSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} diff --git a/atari_py/ale_interface/src/games/supported/UpNDown.hpp b/atari_py/ale_interface/src/games/supported/UpNDown.hpp index ea2e548..2d337f5 100644 --- a/atari_py/ale_interface/src/games/supported/UpNDown.hpp +++ b/atari_py/ale_interface/src/games/supported/UpNDown.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 70 and 77 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -67,7 +67,11 @@ class UpNDownSettings : public RomSettings { // UpNDown requires the fire action to start the game ActionVect getStartingActions(); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 4 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Venture.cpp b/atari_py/ale_interface/src/games/supported/Venture.cpp index b19e229..889ce99 100644 --- a/atari_py/ale_interface/src/games/supported/Venture.cpp +++ b/atari_py/ale_interface/src/games/supported/Venture.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 63, 116, 124 and 132 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -132,3 +132,9 @@ void VentureSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } + +DifficultyVect VentureSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1, 2, 3}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/Venture.hpp b/atari_py/ale_interface/src/games/supported/Venture.hpp index b1914b9..126ab9e 100644 --- a/atari_py/ale_interface/src/games/supported/Venture.hpp +++ b/atari_py/ale_interface/src/games/supported/Venture.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -64,7 +64,11 @@ class VentureSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/VideoPinball.cpp b/atari_py/ale_interface/src/games/supported/VideoPinball.cpp index 99bd443..5d2eacc 100644 --- a/atari_py/ale_interface/src/games/supported/VideoPinball.cpp +++ b/atari_py/ale_interface/src/games/supported/VideoPinball.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 60 - 65, 109, 117 and 125 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -125,3 +125,34 @@ void VideoPinballSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect VideoPinballSettings::getAvailableModes() { + game_mode_t modes[] = {0, 2}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void VideoPinballSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 2) { + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xC1); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(2); + mode = readRam(&system, 0xC1); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect VideoPinballSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} \ No newline at end of file diff --git a/atari_py/ale_interface/src/games/supported/VideoPinball.hpp b/atari_py/ale_interface/src/games/supported/VideoPinball.hpp index 541ba8b..821b65f 100644 --- a/atari_py/ale_interface/src/games/supported/VideoPinball.hpp +++ b/atari_py/ale_interface/src/games/supported/VideoPinball.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class VideoPinballSettings : public RomSettings { // the rom-name const char* rom() const { return "video_pinball"; } + // get the available number of modes + unsigned int getNumModes() const { return 2; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,20 @@ class VideoPinballSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 2 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/WizardOfWor.cpp b/atari_py/ale_interface/src/games/supported/WizardOfWor.cpp index 47ea4b9..eb8a15c 100644 --- a/atari_py/ale_interface/src/games/supported/WizardOfWor.cpp +++ b/atari_py/ale_interface/src/games/supported/WizardOfWor.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 61, 63, 67, 112, 121 and 129 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -48,14 +48,14 @@ RomSettings* WizardOfWorSettings::clone() const { void WizardOfWorSettings::step(const System& system) { // update the reward - reward_t score = getDecimalScore(6, 8, &system); + reward_t score = getDecimalScore(0x86, 0x88, &system); if (score >= 8000) score -= 8000; // MGB score does not go beyond 999 score *= 100; m_reward = score - m_score; m_score = score; // update terminal status - int newLives = readRam(&system, 0x0D) & 15; + int newLives = readRam(&system, 0x8D) & 15; int byte1 = readRam(&system, 0xF4); bool isWaiting = (readRam(&system, 0xD7) & 0x1) == 0; @@ -129,3 +129,8 @@ void WizardOfWorSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +DifficultyVect WizardOfWorSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} + diff --git a/atari_py/ale_interface/src/games/supported/WizardOfWor.hpp b/atari_py/ale_interface/src/games/supported/WizardOfWor.hpp index c5a4834..86f8f8c 100644 --- a/atari_py/ale_interface/src/games/supported/WizardOfWor.hpp +++ b/atari_py/ale_interface/src/games/supported/WizardOfWor.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -64,7 +64,11 @@ class WizardOfWorSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/YarsRevenge.cpp b/atari_py/ale_interface/src/games/supported/YarsRevenge.cpp index 7312057..b754f6b 100644 --- a/atari_py/ale_interface/src/games/supported/YarsRevenge.cpp +++ b/atari_py/ale_interface/src/games/supported/YarsRevenge.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 57, 58, 60, 113, 121 and 129 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -134,3 +134,37 @@ ActionVect YarsRevengeSettings::getStartingActions() { startingActions.push_back(PLAYER_A_FIRE); return startingActions; } + +// returns a list of mode that the game can be played in +ModeVect YarsRevengeSettings::getAvailableModes() { + game_mode_t modes[] = {0, 0x20, 0x40, 0x60}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void YarsRevengeSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + if(m == 0 || m == 0x20 || m == 0x40 || m == 0x60) { + // enter in mode selection screen + environment.pressSelect(2); + // read the mode we are currently in + unsigned char mode = readRam(&system, 0xE3); + // press select until the correct mode is reached + while (mode != m) { + environment.pressSelect(); + mode = readRam(&system, 0xE3); + } + //reset the environment to apply changes. + environment.softReset(); + } + else { + throw std::runtime_error("This mode doesn't currently exist for this game"); + } + } + +DifficultyVect YarsRevengeSettings::getAvailableDifficulties() { + difficulty_t diff[] = {0, 1}; + return DifficultyVect(diff + 0, diff + sizeof(diff)/sizeof(diff[0])); +} \ No newline at end of file diff --git a/atari_py/ale_interface/src/games/supported/YarsRevenge.hpp b/atari_py/ale_interface/src/games/supported/YarsRevenge.hpp index 98413d1..1351e5b 100644 --- a/atari_py/ale_interface/src/games/supported/YarsRevenge.hpp +++ b/atari_py/ale_interface/src/games/supported/YarsRevenge.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 70 and 77 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class YarsRevengeSettings : public RomSettings { // the rom-name const char* rom() const { return "yars_revenge"; } + // get the available number of modes + unsigned int getNumModes() const { return 4; } + // create a new instance of the rom RomSettings* clone() const; @@ -67,7 +70,20 @@ class YarsRevengeSettings : public RomSettings { // Gopher requires the fire action to start the game ActionVect getStartingActions(); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 4 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + + // returns a list of difficulties that the game can be played in + // in this game, there are 2 available difficulties + DifficultyVect getAvailableDifficulties(); private: diff --git a/atari_py/ale_interface/src/games/supported/Zaxxon.cpp b/atari_py/ale_interface/src/games/supported/Zaxxon.cpp index 3a7cc6c..f2607a1 100644 --- a/atari_py/ale_interface/src/games/supported/Zaxxon.cpp +++ b/atari_py/ale_interface/src/games/supported/Zaxxon.cpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 61, 63, 116, 124 and 132 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -132,3 +132,26 @@ void ZaxxonSettings::loadState(Deserializer & ser) { m_lives = ser.getInt(); } +// returns a list of mode that the game can be played in +ModeVect ZaxxonSettings::getAvailableModes() { + game_mode_t modes[] = {0, 8, 16, 24}; + return ModeVect(modes + 0, modes + sizeof(modes)/sizeof(modes[0])); +} + +// set the mode of the game +// the given mode must be one returned by the previous function +void ZaxxonSettings::setMode(game_mode_t m, System &system, + StellaEnvironmentWrapper& environment) { + + // read the mode we are currently in + unsigned char mode = readRam(&system, 0x82); + // press select until the correct mode is reached + while (mode != m) { + // hold select button for 10 frames + environment.pressSelect(10); + mode = readRam(&system, 0x82); + } + //reset the environment to apply changes. + environment.softReset(); + } + diff --git a/atari_py/ale_interface/src/games/supported/Zaxxon.hpp b/atari_py/ale_interface/src/games/supported/Zaxxon.hpp index a1978ff..98c06e2 100644 --- a/atari_py/ale_interface/src/games/supported/Zaxxon.hpp +++ b/atari_py/ale_interface/src/games/supported/Zaxxon.hpp @@ -1,5 +1,5 @@ /* ***************************************************************************** - * The lines 67 and 74 are based on Xitari's code, from Google Inc. + * The method lives() is based on Xitari's code, from Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -49,6 +49,9 @@ class ZaxxonSettings : public RomSettings { // the rom-name const char* rom() const { return "zaxxon"; } + // get the available number of modes + unsigned int getNumModes() const { return 4; } + // create a new instance of the rom RomSettings* clone() const; @@ -64,7 +67,17 @@ class ZaxxonSettings : public RomSettings { // loads the state of the rom settings void loadState(Deserializer & ser); - virtual const int lives() { return isTerminal() ? 0 : m_lives; } + virtual int lives() { return isTerminal() ? 0 : m_lives; } + + // returns a list of mode that the game can be played in + // in this game, there are 4 available modes + ModeVect getAvailableModes(); + + // set the mode of the game + // the given mode must be one returned by the previous function + void setMode(game_mode_t, System &system, + StellaEnvironmentWrapper& environment); + private: diff --git a/atari_py/ale_interface/src/main.cpp b/atari_py/ale_interface/src/main.cpp index 6e9767b..d745698 100644 --- a/atari_py/ale_interface/src/main.cpp +++ b/atari_py/ale_interface/src/main.cpp @@ -24,7 +24,7 @@ #include "common/Defaults.hpp" -#if (defined(WIN32) || defined(__MINGW32__)) +#if (defined(_WIN32) || defined(__MINGW32__)) # include "os_dependent/SettingsWin32.hxx" # include "os_dependent/OSystemWin32.hxx" #else @@ -38,8 +38,9 @@ #include "common/Constants.h" #include "ale_interface.hpp" -static std::auto_ptr theOSystem(NULL); -static std::auto_ptr theSettings(NULL); +// TODO(mgbellemare): Why are these static? +static std::unique_ptr theOSystem; +static std::unique_ptr theSettings; static ALEController* createController(OSystem* osystem, std::string type) { if(type.empty()){ @@ -79,7 +80,7 @@ int main(int argc, char* argv[]) { // Create the game controller std::string controller_type = theOSystem->settings().getString("game_controller"); - std::auto_ptr controller(createController(theOSystem.get(), controller_type)); + std::unique_ptr controller(createController(theOSystem.get(), controller_type)); controller->run(); diff --git a/atari_py/ale_interface/src/os_dependent/OSystemUNIX.cxx b/atari_py/ale_interface/src/os_dependent/OSystemUNIX.cxx index 60cfcbf..9b31aca 100644 --- a/atari_py/ale_interface/src/os_dependent/OSystemUNIX.cxx +++ b/atari_py/ale_interface/src/os_dependent/OSystemUNIX.cxx @@ -51,7 +51,7 @@ OSystemUNIX::OSystemUNIX() //ALE const string& basedir = string(getenv("HOME")) + "/.stella"; string basedir = string("."); //ALE setBaseDir(basedir); - setConfigFile(basedir + "/stellarc"); + setConfigFile(basedir + "/ale.cfg"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/atari_py/ale_interface/src/os_dependent/OSystemWin32.cxx b/atari_py/ale_interface/src/os_dependent/OSystemWin32.cxx index f7164a9..11a4dd8 100644 --- a/atari_py/ale_interface/src/os_dependent/OSystemWin32.cxx +++ b/atari_py/ale_interface/src/os_dependent/OSystemWin32.cxx @@ -47,7 +47,7 @@ OSystemWin32::OSystemWin32() // For now, we just put it in the same directory as the executable. const string& basedir = "."; setBaseDir(basedir); - setConfigFile(basedir + "\\stellarc"); + setConfigFile(basedir + "\\ale.cfg"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/atari_py/ale_interface/stellarc b/atari_py/ale_interface/stellarc deleted file mode 100644 index 79f94d2..0000000 --- a/atari_py/ale_interface/stellarc +++ /dev/null @@ -1,2 +0,0 @@ -cpu=low - diff --git a/atari_py/ale_python_interface.py b/atari_py/ale_python_interface.py index fd2280a..d4f4cb5 100644 --- a/atari_py/ale_python_interface.py +++ b/atari_py/ale_python_interface.py @@ -2,14 +2,30 @@ # Author: Ben Goodrich # This directly implements a python version of the arcade learning # environment interface. +__all__ = ['ALEInterface'] from ctypes import * import numpy as np from numpy.ctypeslib import as_ctypes import os -ale_lib = cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), - 'ale_interface/build/libale_c.so')) + +def get_shared_lib_path(): + from distutils.ccompiler import get_default_compiler, new_compiler + from distutils.sysconfig import get_config_var + if os.name == 'nt': + fname = 'ale_c' + else: + fname = 'libale_c' + ext_suffix = get_config_var('EXT_SUFFIX') + if ext_suffix is not None: + fname += os.path.splitext(ext_suffix)[0] + fname += new_compiler(compiler=get_default_compiler()).shared_lib_extension + path = os.path.join(os.path.dirname(__file__), '..', fname) + return os.path.abspath(path) + + +ale_lib = cdll.LoadLibrary(get_shared_lib_path()) ale_lib.ALE_new.argtypes = None ale_lib.ALE_new.restype = c_void_p @@ -23,7 +39,7 @@ ale_lib.getBool.restype = c_bool ale_lib.getFloat.argtypes = [c_void_p, c_char_p] ale_lib.getFloat.restype = c_float -ale_lib.setString.argtypes = [c_void_p, c_char_p, c_int] +ale_lib.setString.argtypes = [c_void_p, c_char_p, c_char_p] ale_lib.setString.restype = None ale_lib.setInt.argtypes = [c_void_p, c_char_p, c_int] ale_lib.setInt.restype = None @@ -31,14 +47,26 @@ ale_lib.setBool.restype = None ale_lib.setFloat.argtypes = [c_void_p, c_char_p, c_float] ale_lib.setFloat.restype = None -ale_lib.loadROM.argtypes = [c_void_p, c_char_p] -ale_lib.loadROM.restype = None +ale_lib.loadROM.argtypes = [c_void_p, c_char_p, c_int, c_char_p] +ale_lib.loadROM.restype = c_bool ale_lib.act.argtypes = [c_void_p, c_int] ale_lib.act.restype = c_int ale_lib.game_over.argtypes = [c_void_p] ale_lib.game_over.restype = c_bool ale_lib.reset_game.argtypes = [c_void_p] ale_lib.reset_game.restype = None +ale_lib.getAvailableModes.argtypes = [c_void_p, c_void_p] +ale_lib.getAvailableModes.restype = None +ale_lib.getAvailableModesSize.argtypes = [c_void_p] +ale_lib.getAvailableModesSize.restype = c_int +ale_lib.setMode.argtypes = [c_void_p, c_int] +ale_lib.setMode.restype = None +ale_lib.getAvailableDifficulties.argtypes = [c_void_p, c_void_p] +ale_lib.getAvailableDifficulties.restype = None +ale_lib.getAvailableDifficultiesSize.argtypes = [c_void_p] +ale_lib.getAvailableDifficultiesSize.restype = c_int +ale_lib.setDifficulty.argtypes = [c_void_p, c_int] +ale_lib.setDifficulty.restype = None ale_lib.getLegalActionSet.argtypes = [c_void_p, c_void_p] ale_lib.getLegalActionSet.restype = None ale_lib.getLegalActionSize.argtypes = [c_void_p] @@ -65,76 +93,78 @@ ale_lib.getScreenHeight.restype = c_int ale_lib.getScreenRGB.argtypes = [c_void_p, c_void_p] ale_lib.getScreenRGB.restype = None +ale_lib.getScreenRGB2.argtypes = [c_void_p, c_void_p] +ale_lib.getScreenRGB2.restype = None ale_lib.getScreenGrayscale.argtypes = [c_void_p, c_void_p] ale_lib.getScreenGrayscale.restype = None ale_lib.saveState.argtypes = [c_void_p] ale_lib.saveState.restype = None ale_lib.loadState.argtypes = [c_void_p] ale_lib.loadState.restype = None -ale_lib.saveScreenPNG.argtypes = [c_void_p, c_char_p] -ale_lib.saveScreenPNG.restype = None ale_lib.cloneState.argtypes = [c_void_p] ale_lib.cloneState.restype = c_void_p ale_lib.restoreState.argtypes = [c_void_p, c_void_p] ale_lib.restoreState.restype = None - -ale_lib.ALEState_del.argtypes = [c_void_p] -ale_lib.ALEState_del.restype = None -ale_lib.ALEState_getFrameNumber.argtypes = [c_void_p] -ale_lib.ALEState_getFrameNumber.restype = c_int -ale_lib.ALEState_getEpisodeFrameNumber.argtypes = [c_void_p] -ale_lib.ALEState_getEpisodeFrameNumber.restype = c_int -ale_lib.ALEState_equals.argtypes = [c_void_p, c_void_p] -ale_lib.ALEState_equals.restype = c_bool - - -class ALEState(object): - def __init__(self, obj, ale): - self.obj = obj - self.ale = ale - - def __del__(self): - ale_lib.ALEState_del(self.obj) - - def __eq__(self, other): - return isinstance(other, self.__class__) \ - and ale_lib.ALEState_equals(self.obj, other.obj) - - def __ne__(self, other): - return not self.__eq__(other) - - @property - def frame_number(self): - return ale_lib.ALEState_getFrameNumber(self.obj) - - @property - def episode_frame_number(self): - return ale_lib.ALEState_getEpisodeFrameNumber(self.obj) +ale_lib.cloneSystemState.argtypes = [c_void_p] +ale_lib.cloneSystemState.restype = c_void_p +ale_lib.restoreSystemState.argtypes = [c_void_p, c_void_p] +ale_lib.restoreSystemState.restype = None +ale_lib.deleteState.argtypes = [c_void_p] +ale_lib.deleteState.restype = None +'''Kojoley +ale_lib.saveScreenPNG.argtypes = [c_void_p, c_char_p] +ale_lib.saveScreenPNG.restype = None +Kojoley''' +ale_lib.encodeState.argtypes = [c_void_p, c_void_p, c_int] +ale_lib.encodeState.restype = None +ale_lib.encodeStateLen.argtypes = [c_void_p] +ale_lib.encodeStateLen.restype = c_int +ale_lib.decodeState.argtypes = [c_void_p, c_int] +ale_lib.decodeState.restype = c_void_p +ale_lib.setLoggerMode.argtypes = [c_int] +ale_lib.setLoggerMode.restype = None + +def _as_bytes(s): + if hasattr(s, 'encode'): + return s.encode('utf8') + return s class ALEInterface(object): + # Logger enum + class Logger: + Info = 0 + Warning = 1 + Error = 2 + def __init__(self): self.obj = ale_lib.ALE_new() def getString(self, key): - return ale_lib.getString(self.obj, key) + return ale_lib.getString(self.obj, _as_bytes(key)) def getInt(self, key): - return ale_lib.getInt(self.obj, key) + return ale_lib.getInt(self.obj, _as_bytes(key)) def getBool(self, key): - return ale_lib.getBool(self.obj, key) + return ale_lib.getBool(self.obj, _as_bytes(key)) def getFloat(self, key): - return ale_lib.getFloat(self.obj, key) + return ale_lib.getFloat(self.obj, _as_bytes(key)) def setString(self, key, value): - ale_lib.setString(self.obj, key, value) + ale_lib.setString(self.obj, _as_bytes(key), _as_bytes(value)) def setInt(self, key, value): - ale_lib.setInt(self.obj, key, value) + ale_lib.setInt(self.obj, _as_bytes(key), int(value)) def setBool(self, key, value): - ale_lib.setBool(self.obj, key, value) + ale_lib.setBool(self.obj, _as_bytes(key), bool(value)) def setFloat(self, key, value): - ale_lib.setFloat(self.obj, key, value) + ale_lib.setFloat(self.obj, _as_bytes(key), float(value)) def loadROM(self, rom_file): - ale_lib.loadROM(self.obj, rom_file) + name, _ = os.path.splitext(os.path.basename(rom_file)) + if not isinstance(name, bytes): + name = name.encode('utf-8') + with open(rom_file, 'rb') as f: + data = f.read() + if not ale_lib.loadROM(self.obj, data, len(data), name): + raise ValueError("Failed to load ROM") def act(self, action): return ale_lib.act(self.obj, int(action)) @@ -157,6 +187,36 @@ def getMinimalActionSet(self): ale_lib.getMinimalActionSet(self.obj, as_ctypes(act)) return act + def getAvailableModes(self): + modes_size = ale_lib.getAvailableModesSize(self.obj) + modes = np.zeros((modes_size), dtype=np.intc) + ale_lib.getAvailableModes(self.obj, as_ctypes(modes)) + return modes + + def setMode(self, mode): + ale_lib.setMode(self.obj, int(mode)) + + def getAvailableDifficulties(self): + difficulties_size = ale_lib.getAvailableDifficultiesSize(self.obj) + difficulties = np.zeros((difficulties_size), dtype=np.intc) + ale_lib.getAvailableDifficulties(self.obj, as_ctypes(difficulties)) + return difficulties + + def setDifficulty(self, difficulty): + ale_lib.setDifficulty(self.obj, int(difficulty)) + + def getLegalActionSet(self): + act_size = ale_lib.getLegalActionSize(self.obj) + act = np.zeros((act_size), dtype=np.intc) + ale_lib.getLegalActionSet(self.obj, as_ctypes(act)) + return act + + def getMinimalActionSet(self): + act_size = ale_lib.getMinimalActionSize(self.obj) + act = np.zeros((act_size), dtype=np.intc) + ale_lib.getMinimalActionSet(self.obj, as_ctypes(act)) + return act + def getFrameNumber(self): return ale_lib.getFrameNumber(self.obj) @@ -173,7 +233,6 @@ def getScreenDims(self): height = ale_lib.getScreenHeight(self.obj) return (width, height) - def getScreen(self, screen_data=None): """This function fills screen_data with the RAW Pixel data screen_data MUST be a numpy array of uint8/int8. This could be initialized like so: @@ -194,6 +253,11 @@ def getScreenRGB(self, screen_data=None): screen_data MUST be a numpy array of uint8. This can be initialized like so: screen_data = np.empty((height,width,3), dtype=np.uint8) If it is None, then this function will initialize it. + On little-endian machines like x86, the channels are BGR order: + screen_data[x, y, 0:3] is [blue, green, red] + On big-endian machines (rare in 2017) the channels would be the opposite order. + There's not much error checking here: if you supply an array that's too small + this function will produce undefined behavior. """ if(screen_data is None): width = ale_lib.getScreenWidth(self.obj) @@ -202,6 +266,24 @@ def getScreenRGB(self, screen_data=None): ale_lib.getScreenRGB(self.obj, as_ctypes(screen_data[:])) return screen_data + def getScreenRGB2(self, screen_data=None): + """This function fills screen_data with the data in RGB format. + screen_data MUST be a numpy array of uint8. This can be initialized like so: + screen_data = np.empty((height,width,3), dtype=np.uint8) + If it is None, then this function will initialize it. + On all architectures, the channels are RGB order: + screen_data[x, y, :] is [red, green, blue] + There's not much error checking here: if you supply an array that's too small + this function will produce undefined behavior. + """ + if(screen_data is None): + width = ale_lib.getScreenWidth(self.obj) + height = ale_lib.getScreenHeight(self.obj) + screen_data = np.empty((height, width, 3), dtype=np.uint8) + assert screen_data.strides == (480, 3, 1) + ale_lib.getScreenRGB2(self.obj, as_ctypes(screen_data[:])) + return screen_data + def getScreenGrayscale(self, screen_data=None): """This function fills screen_data with the data in grayscale screen_data MUST be a numpy array of uint8. This can be initialized like so: @@ -232,21 +314,68 @@ def getRAM(self, ram=None): return ram def saveScreenPNG(self, filename): - return ale_lib.saveScreenPNG(self.obj, filename) + """Save the current screen as a png file""" + from PIL import Image + im = Image.fromarray(self.getScreenRGB()) + im.save(filename, 'png') def saveState(self): + """Saves the state of the system""" return ale_lib.saveState(self.obj) def loadState(self): + """Loads the state of the system""" return ale_lib.loadState(self.obj) def cloneState(self): - return ALEState(ale_lib.cloneState(self.obj), self) + """This makes a copy of the environment state. This copy does *not* + include pseudorandomness, making it suitable for planning + purposes. By contrast, see cloneSystemState. + """ + return ale_lib.cloneState(self.obj) def restoreState(self, state): - if state.ale is not self: - raise ValueError('Can only restore state on the ale instance it yielded from') - return ale_lib.restoreState(self.obj, state.obj) + """Reverse operation of cloneState(). This does not restore + pseudorandomness, so that repeated calls to restoreState() in + the stochastic controls setting will not lead to the same + outcomes. By contrast, see restoreSystemState. + """ + ale_lib.restoreState(self.obj, state) + + def cloneSystemState(self): + """This makes a copy of the system & environment state, suitable for + serialization. This includes pseudorandomness and so is *not* + suitable for planning purposes. + """ + return ale_lib.cloneSystemState(self.obj) + + def restoreSystemState(self, state): + """Reverse operation of cloneSystemState.""" + ale_lib.restoreSystemState(self.obj, state) + + def deleteState(self, state): + """ Deallocates the ALEState """ + ale_lib.deleteState(state) + + def encodeStateLen(self, state): + return ale_lib.encodeStateLen(state) + + def encodeState(self, state, buf=None): + if buf == None: + length = ale_lib.encodeStateLen(state) + buf = np.zeros(length, dtype=np.uint8) + ale_lib.encodeState(state, as_ctypes(buf), c_int(len(buf))) + return buf + + def decodeState(self, serialized): + return ale_lib.decodeState(as_ctypes(serialized), len(serialized)) def __del__(self): ale_lib.ALE_del(self.obj) + + @staticmethod + def setLoggerMode(mode): + dic = {'info': 0, 'warning': 1, 'error': 2} + mode = dic.get(mode, mode) + assert mode in [0, 1, 2], "Invalid Mode! Mode must be one of 0: info, 1: warning, 2: error" + ale_lib.setLoggerMode(mode) diff --git a/atari_py/atari_roms/adventure.bin b/atari_py/atari_roms/adventure.bin new file mode 100644 index 0000000..a092248 Binary files /dev/null and b/atari_py/atari_roms/adventure.bin differ diff --git a/atari_py/atari_roms/hero.bin b/atari_py/atari_roms/hero.bin new file mode 100644 index 0000000..4242bd9 Binary files /dev/null and b/atari_py/atari_roms/hero.bin differ diff --git a/atari_py/atari_roms/kaboom.bin b/atari_py/atari_roms/kaboom.bin new file mode 100644 index 0000000..4f18cb0 Binary files /dev/null and b/atari_py/atari_roms/kaboom.bin differ diff --git a/atari_py/package_data.txt b/atari_py/package_data.txt index d1e27aa..574dcba 100644 --- a/atari_py/package_data.txt +++ b/atari_py/package_data.txt @@ -1,13 +1,7 @@ -ale_interface/build/*.so -ale_interface/build/ale ale_c_wrapper.cpp ale_c_wrapper.h ale_interface/CMakeLists.txt ale_interface/Makefile -ale_interface/common.rules -ale_interface/makefile.mac -ale_interface/makefile.unix -ale_interface/setup.sh ale_interface/src/ale_interface.cpp ale_interface/src/ale_interface.hpp ale_interface/src/common/Array.hxx @@ -15,8 +9,6 @@ ale_interface/src/common/ColourPalette.cpp ale_interface/src/common/ColourPalette.hpp ale_interface/src/common/Constants.cpp ale_interface/src/common/Constants.h -ale_interface/src/common/Defaults.cpp -ale_interface/src/common/Defaults.hpp ale_interface/src/common/Log.cpp ale_interface/src/common/Log.hpp ale_interface/src/common/Palettes.hpp @@ -186,8 +178,6 @@ ale_interface/src/emucore/rsynth/trie.c ale_interface/src/emucore/rsynth/trie.h ale_interface/src/emucore/rsynth/useconfig.h ale_interface/src/emucore/stella.pro -ale_interface/src/emucore/unzip.c -ale_interface/src/emucore/unzip.h ale_interface/src/environment/ale_ram.hpp ale_interface/src/environment/ale_screen.hpp ale_interface/src/environment/ale_state.cpp @@ -197,6 +187,8 @@ ale_interface/src/environment/phosphor_blend.cpp ale_interface/src/environment/phosphor_blend.hpp ale_interface/src/environment/stella_environment.cpp ale_interface/src/environment/stella_environment.hpp +ale_interface/src/environment/stella_environment_wrapper.cpp +ale_interface/src/environment/stella_environment_wrapper.hpp ale_interface/src/external/TinyMT/LICENSE.txt ale_interface/src/external/TinyMT/tinymt32.c ale_interface/src/external/TinyMT/tinymt32.h @@ -208,6 +200,8 @@ ale_interface/src/games/RomUtils.hpp ale_interface/src/games/Roms.cpp ale_interface/src/games/Roms.hpp ale_interface/src/games/module.mk +ale_interface/src/games/supported/Adventure.cpp +ale_interface/src/games/supported/Adventure.hpp ale_interface/src/games/supported/AirRaid.cpp ale_interface/src/games/supported/AirRaid.hpp ale_interface/src/games/supported/Alien.cpp @@ -248,6 +242,8 @@ ale_interface/src/games/supported/Defender.cpp ale_interface/src/games/supported/Defender.hpp ale_interface/src/games/supported/DemonAttack.cpp ale_interface/src/games/supported/DemonAttack.hpp +ale_interface/src/games/supported/DonkeyKong.cpp +ale_interface/src/games/supported/DonkeyKong.hpp ale_interface/src/games/supported/DoubleDunk.cpp ale_interface/src/games/supported/DoubleDunk.hpp ale_interface/src/games/supported/ElevatorAction.cpp @@ -258,8 +254,12 @@ ale_interface/src/games/supported/FishingDerby.cpp ale_interface/src/games/supported/FishingDerby.hpp ale_interface/src/games/supported/Freeway.cpp ale_interface/src/games/supported/Freeway.hpp +ale_interface/src/games/supported/Frogger.cpp +ale_interface/src/games/supported/Frogger.hpp ale_interface/src/games/supported/Frostbite.cpp ale_interface/src/games/supported/Frostbite.hpp +ale_interface/src/games/supported/Galaxian.cpp +ale_interface/src/games/supported/Galaxian.hpp ale_interface/src/games/supported/Gopher.cpp ale_interface/src/games/supported/Gopher.hpp ale_interface/src/games/supported/Gravitar.cpp @@ -272,14 +272,28 @@ ale_interface/src/games/supported/JamesBond.cpp ale_interface/src/games/supported/JamesBond.hpp ale_interface/src/games/supported/JourneyEscape.cpp ale_interface/src/games/supported/JourneyEscape.hpp +ale_interface/src/games/supported/Kaboom.cpp +ale_interface/src/games/supported/Kaboom.hpp ale_interface/src/games/supported/Kangaroo.cpp ale_interface/src/games/supported/Kangaroo.hpp +ale_interface/src/games/supported/KeystoneKapers.cpp +ale_interface/src/games/supported/KeystoneKapers.hpp +ale_interface/src/games/supported/Kingkong.cpp +ale_interface/src/games/supported/Kingkong.hpp +ale_interface/src/games/supported/Koolaid.cpp +ale_interface/src/games/supported/Koolaid.hpp ale_interface/src/games/supported/Krull.cpp ale_interface/src/games/supported/Krull.hpp ale_interface/src/games/supported/KungFuMaster.cpp ale_interface/src/games/supported/KungFuMaster.hpp +ale_interface/src/games/supported/LaserGates.cpp +ale_interface/src/games/supported/LaserGates.hpp +ale_interface/src/games/supported/LostLuggage.cpp +ale_interface/src/games/supported/LostLuggage.hpp ale_interface/src/games/supported/MontezumaRevenge.cpp ale_interface/src/games/supported/MontezumaRevenge.hpp +ale_interface/src/games/supported/MrDo.cpp +ale_interface/src/games/supported/MrDo.hpp ale_interface/src/games/supported/MsPacman.cpp ale_interface/src/games/supported/MsPacman.hpp ale_interface/src/games/supported/NameThisGame.cpp @@ -304,6 +318,8 @@ ale_interface/src/games/supported/RoboTank.cpp ale_interface/src/games/supported/RoboTank.hpp ale_interface/src/games/supported/Seaquest.cpp ale_interface/src/games/supported/Seaquest.hpp +ale_interface/src/games/supported/SirLancelot.cpp +ale_interface/src/games/supported/SirLancelot.hpp ale_interface/src/games/supported/Skiing.cpp ale_interface/src/games/supported/Skiing.hpp ale_interface/src/games/supported/Solaris.cpp @@ -314,8 +330,14 @@ ale_interface/src/games/supported/StarGunner.cpp ale_interface/src/games/supported/StarGunner.hpp ale_interface/src/games/supported/Tennis.cpp ale_interface/src/games/supported/Tennis.hpp +ale_interface/src/games/supported/Tetris.cpp +ale_interface/src/games/supported/Tetris.hpp ale_interface/src/games/supported/TimePilot.cpp ale_interface/src/games/supported/TimePilot.hpp +ale_interface/src/games/supported/Trondead.cpp +ale_interface/src/games/supported/Trondead.hpp +ale_interface/src/games/supported/Turmoil.cpp +ale_interface/src/games/supported/Turmoil.hpp ale_interface/src/games/supported/Tutankham.cpp ale_interface/src/games/supported/Tutankham.hpp ale_interface/src/games/supported/UpNDown.cpp @@ -343,8 +365,8 @@ ale_interface/src/os_dependent/SettingsUNIX.hxx ale_interface/src/os_dependent/SettingsWin32.cxx ale_interface/src/os_dependent/SettingsWin32.hxx ale_interface/src/os_dependent/module.mk -ale_interface/stellarc atari_ntsc_rgb_palette.h +atari_roms/adventure.bin atari_roms/air_raid.bin atari_roms/alien.bin atari_roms/amidar.bin @@ -373,9 +395,11 @@ atari_roms/freeway.bin atari_roms/frostbite.bin atari_roms/gopher.bin atari_roms/gravitar.bin +atari_roms/hero.bin atari_roms/ice_hockey.bin atari_roms/jamesbond.bin atari_roms/journey_escape.bin +atari_roms/kaboom.bin atari_roms/kangaroo.bin atari_roms/krull.bin atari_roms/kung_fu_master.bin @@ -406,3 +430,4 @@ atari_roms/wizard_of_wor.bin atari_roms/yars_revenge.bin atari_roms/zaxxon.bin package_data.txt +tests/*.py diff --git a/atari_py/tests/__init__.py b/atari_py/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/atari_py/tests/test_smoke.py b/atari_py/tests/test_smoke.py new file mode 100644 index 0000000..d2f54ec --- /dev/null +++ b/atari_py/tests/test_smoke.py @@ -0,0 +1,21 @@ +import atari_py +import numpy as np + +def test_smoke(): + pong_path = atari_py.get_game_path('pong') + ale = atari_py.ALEInterface() + ale.loadROM(pong_path) + action_set = ale.getMinimalActionSet() + + # Test stepping + ale.act(action_set[0]) + + # Test screen capture + (screen_width,screen_height) = ale.getScreenDims() + arr = np.zeros((screen_height, screen_width, 4), dtype=np.uint8) + ale.getScreenRGB(arr) + +if __name__ == '__main__': + print('smoke test') + test_smoke() + print('done!') diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..a16a252 --- /dev/null +++ b/build.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -ex + +if [ $(uname) == 'Linux' ] || [ $(uname) == 'Darwin' ]; then + ./multibuild.sh +else + ./win_build.sh +fi diff --git a/config.sh b/config.sh new file mode 100644 index 0000000..47e0bfd --- /dev/null +++ b/config.sh @@ -0,0 +1,14 @@ +#!/bin/bash +function pre_build { + set -ex + build_zlib + pip install cmake +} + +function run_tests { + pip install gym + python -c "import gym; gym.make('Pong-v4')" + pip install pytest + pytest --pyargs atari_py.tests +} + diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..d03b182 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,11 @@ +set -ex +if [[ ! -z "$TRAVIS_TAG" ]]; then + pip install twine + twine upload --verbose wheelhouse/atari_py* + + if [[ ! -z "$DEPLOY_SDIST" ]]; then + python setup.py sdist + twine upload dist/* + fi +fi + diff --git a/deploy_to_pypi.sh b/deploy_to_pypi.sh new file mode 100755 index 0000000..6545c49 --- /dev/null +++ b/deploy_to_pypi.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -ex + +# TRAVIS_TAG=0.1.14 + +if [[ -z "$TRAVIS_TAG" ]]; then + echo "Not a tagged commit, quitting" + exit 0 +fi + +pip install virtualenv --user +python -m virtualenv --python=python3 .venv +source .venv/bin/activate + +pip install awscli twine +mkdir -p wheelhouse +env +# ugh something in awscli does not work on travis we'll download objects via curl +# aws s3 cp --recursive s3://games-wheels/atari-py/${TRAVIS_TAG} wheelhouse/ +SUFFIXES="-manylinux1_x86_64.whl -win_amd64.whl -macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl" +# SUFFIXES="-manylinux1_x86_64.whl" +PY_VERS="-cp36-cp36m -cp37-cp37m" +URLPREFIX="https://s3-us-west-2.amazonaws.com/games-wheels/atari-py/${TRAVIS_TAG}/atari_py-${TRAVIS_TAG}" + +cd wheelhouse +for s in $SUFFIXES; do + for p in $PY_VERS; do + curl -O ${URLPREFIX}${p}${s} + done; +done + +twine upload --verbose atari_py* diff --git a/doc/examples/python_example.py b/doc/examples/python_example.py index 7b9a359..946328f 100755 --- a/doc/examples/python_example.py +++ b/doc/examples/python_example.py @@ -4,12 +4,14 @@ # # This is a direct port to python of the shared library example from # ALE provided in doc/examples/sharedLibraryInterfaceExample.cpp +from __future__ import print_function + import sys from random import randrange -from ale_python_interface import ALEInterface +from atari_py import ALEInterface if len(sys.argv) < 2: - print 'Usage:', sys.argv[0], 'rom_file' + print('Usage:', sys.argv[0], 'rom_file') sys.exit() ale = ALEInterface() @@ -37,12 +39,12 @@ legal_actions = ale.getLegalActionSet() # Play 10 episodes -for episode in xrange(10): +for episode in range(10): total_reward = 0 while not ale.game_over(): a = legal_actions[randrange(len(legal_actions))] # Apply an action and get the resulting reward reward = ale.act(a); total_reward += reward - print 'Episode', episode, 'ended with score:', total_reward + print('Episode', episode, 'ended with score:', total_reward) ale.reset_game() diff --git a/doc/manual/manual.pdf b/doc/manual/manual.pdf index ef1ab38..12e67c6 100644 Binary files a/doc/manual/manual.pdf and b/doc/manual/manual.pdf differ diff --git a/doc/manual/manual.tex b/doc/manual/manual.tex index ab628db..fa4d48a 100644 --- a/doc/manual/manual.tex +++ b/doc/manual/manual.tex @@ -5,7 +5,7 @@ \usepackage{hyperref} \usepackage{fullpage} -\title{Arcade Learning Environment\\ Technical Manual (v.0.5.0)} +\title{Arcade Learning Environment\\ Technical Manual (v.0.5.1)} \author{Marlos C. Machado, Matthew Hausknecht, Marc G. Bellemare} \begin{document} @@ -20,7 +20,7 @@ \section{Overview} -This document is roughly divided into three parts. The first part describes how to install the Arcade Learning Environment. The second part describes the various ALE interfaces currently available: +This document is roughly divided into three parts. The first part describes how to install the Arcade Learning Environment (ALE). The second part describes the various ALE interfaces currently available: \begin{enumerate} \item \textbf{Shared Library interface} (C++ only): Loads ALE as a shared library (Section \ref{sec:shared_library_interface}). @@ -103,7 +103,8 @@ \section{C++ Agent: Shared Library Interface} \label{sec:shared_library_interfac Once the environment is initialized, it is now possible to set its arguments. This is done with the functions \verb+setBool(), setInt(), setFloat()+. The complete list of flags is available in -Section~\ref{sec:arguments}. Just as an example, to set the environment's seed we write \\ +Section~\ref{sec:arguments}. Just as an example, to set the environment's seed we write:\\ + \verb+ale.setInt("random_seed", 123);+\\ Finally, after setting the desired environment parameters we now load the game ROM by providing its filename to the \verb+loadROM+ method:\\ @@ -159,7 +160,7 @@ \section{C++ Agent: Shared Library Interface} \label{sec:shared_library_interfac \begin{center} \verb+doc/examples/sharedLibraryExample.cpp+\end{center} -example for more details, including compilation, as +\noindent example for more details, including compilation, as well as Section \ref{sec:troubleshooting} if errors arise. A complete list of functions available in the class \verb+ALEInterface+ is given in Section~\ref{sec:functions}. @@ -169,7 +170,7 @@ \section{Python Agent: CTypes Interface}\label{sec:python_interface} \verb+pip install .+\\ -without root/superuser access: +\noindent without root/superuser access: \verb+pip install --user .+\\ @@ -216,15 +217,13 @@ \subsection{Handshaking} www-hhh\n \end{verbatim} -where \verb+www+ and \verb+hhh+ are both integers. - -The agent then responds with a comma-separated string: +\noindent where \verb+www+ and \verb+hhh+ are both integers. The agent then responds with a comma-separated string: \begin{verbatim} s,r,k,R\n \end{verbatim} -where \verb+s+, \verb+r+, \verb+R+ are 1 or 0 to indicate that ALE should or should not send, at every time step, screen, RAM and episode-related information (see below for details). The third argument, \verb+k+, is deprecated and currently ignored. +\noindent where \verb+s+, \verb+r+, \verb+R+ are 1 or 0 to indicate that ALE should or should not send, at every time step, screen, RAM and episode-related information (see below for details). The third argument, \verb+k+, is deprecated and currently ignored. \subsection{Main Loop -- ALE} @@ -253,7 +252,7 @@ \subsubsection{\texttt{screen\_string}} In ``full'' mode, the screen string is \texttt{www} $\times$ \texttt{hhh} 2-digit hexadecimal numbers, each representing a pixel. Pixels are sent row by row, with \texttt{www} characters for each row. In total this string is 2 $\times$ \texttt{www} $\times$ \texttt{hhh} characters long. -In run-length encoding mode, the screen string consists of a variable number of (colour, length) pairs denoting a run-length encoding of the screen, also row by row. Both colour and length are described using 2-digit hexadecimal numbers. Each pair indicates that the next `length' pixels take on the given colour; run length is thus limited to 255. Runs may wrap around onto the next row. The encoding terminates when the last pixel (i.e. the bottom-right pixel) is encoded. The length of this string is 4 characters per (colour,length) pair, and varies depending on the screen. +In run-length encoding mode, the screen string consists of a variable number of (colour, length) pairs denoting a run-length encoding of the screen, also row by row. Both colour and length are described using 2-digit hexadecimal numbers. Each pair indicates that the next `length' pixels take on the given colour; run length is thus limited to 255. Runs may wrap around onto the next row. The encoding terminates when the last pixel (i.e. the bottom-right pixel) is encoded. The length of this string is 4 characters per (colour, length) pair, and varies depending on the screen. In either case, the screen string is terminated by a colon. @@ -280,7 +279,7 @@ \subsection{Main Loop -- Agent} 2,18\n \end{verbatim} -where the first integer is player A's action (here, \textsc{fire}) and the second integer, player B's action (here, \textsc{noop}). Emulator control (reset, save/load state) is also handled by sending a special action value as player A's action. See Section \ref{sec:available_actions} for the list of available actions. +\noindent where the first integer is player A's action (here, \textsc{fire}) and the second integer, player B's action (here, \textsc{noop}). Emulator control (reset, save/load state) is also handled by sending a special action value as player A's action. See Section \ref{sec:available_actions} for the list of available actions. \subsection{Termination}\label{subsec:termination_conditions} @@ -288,14 +287,14 @@ \subsection{Termination}\label{subsec:termination_conditions} \begin{itemize} \item{\texttt{stdin} is closed, indicating that the agent is no longer sending data, or} - \item{The maximum number of frames (user-specified, with no maximum by default) has been reached.} + \item{the maximum number of frames (user-specified, with no maximum by default) has been reached.} \end{itemize} ALE will send an end-of-episode signal when one the following is true: \begin{itemize} \item{The maximum number of frames for this episode (user-specified, with no maximum by default) has been reached, or} - \item{The game has ended, usually when player A loses their last life.} + \item{the game has ended, usually when player A loses their last life.} \end{itemize} \section{RL-Glue Interface}\label{sec:rlglue_interface} @@ -332,12 +331,12 @@ \subsection{Sample Agent and Experiment} You will then need to start the following processes to run the sample RL-Glue agent in ALE: -\begin{itemize} - \item{\verb+> rl_glue+} - \item{\verb+ale_0_5> doc/examples/RLGlueAgent+} - \item{\verb+ale_0_5> doc/examples/RLGlueExperiment+} - \item{\verb+ale_0_5> ./ale -game_controller rlglue+} -\end{itemize} +\begin{verbatim} +ale_0_5> rl_glue +ale_0_5> doc/examples/RLGlueAgent +ale_0_5> doc/examples/RLGlueExperiment +ale_0_5> ./ale -game_controller rlglue +\end{verbatim} Additional options (such as those described below) can also be passed as command-line arguments. Please refer to the RL-Glue documentation for more details. @@ -386,7 +385,7 @@ \subsection{Available Actions}\label{sec:available_actions} \end{center} Note that the \verb+reset+ (40) action toggles the Atari 2600 switch, rather than reset the -environment, and as such is ignored by most interfaces. The Shared library, CTypes, and fifo +environment, and as such is ignored by most interfaces. The shared library, CTypes, and fifo interfaces provide methods for properly resetting the environment. Player B's actions are defined to be 18 + the equivalent action value for Player A. For example, Player B's up action is \verb+up+ (20). @@ -443,7 +442,7 @@ \subsection{Action Repeat Stochasticity} The motivation for introducing action repeat stochasticity was to help separate \emph{trajectory optimization} research from \emph{robust controller optimization}, the latter often being the desired outcome in reinforcement learning (RL). We strongly encourage RL researchers to use -the default stochasticity level their agents, and clearly report on the setting used. More +the default stochasticity level in their agents, and clearly report the setting used. More details on the effects of action repeat stochasticity will be made available in future publications. @@ -456,7 +455,7 @@ \subsection{Minimal Action Set} accomplished by querying the \verb+RomSettings+ class using the method \verb+getMinimalActionSet+. This then returns a set of actions judged ``minimal'' to play a given game. Due to the potentially high impact of this setting on performance, we encourage researchers -to clearly report on the method used in their experiments. +to clearly report the method used in their experiments. \section{Miscellaneous} @@ -617,6 +616,9 @@ \section{Troubleshooting / FAQ} \label{sec:troubleshooting} Make sure your code contains \verb+#include + and that \verb+__USE_SDL+ is defined. Also, compiling under Mac OS X requires the inclusion of the flag \verb+-framework Cocoa+. See the shared library example for a working example. +\item The Python interface isn't working! It can't find \verb+libale_c.so+. + +Make sure to compile ALE using CMake. This will build the library required by the Python interface. \end{itemize} @@ -652,7 +654,7 @@ \section{ALE Interface Specifications}\label{sec:functions} a float value; \emph{e.g.}: \verb+getBool(``repeat_action_probability'')+. \verb+void setString(const string& key, const string& value)+: Sets the value of any flag - that has a string type; \emph{e.g.}: \verb+setString("random_seed", "time")+. + that has a string type; \emph{e.g.}: \verb+setString("record_screen_dir", "record")+. \verb+loadRom()+ must be called before the setting will take effect. \verb+void setInt(const std::string& key, const int value)+: Sets the value of any flag @@ -695,6 +697,15 @@ \section{ALE Interface Specifications}\label{sec:functions} \verb+const ALEScreen &getScreen()+: Returns a matrix containing the current game screen. + \verb+void getScreenGrayscale(pixel_t *grayscale_output_buffer)+: This method should receive an + array of length width $\times$ height (generally $160 \times 210 = 33,600$) and then it will fill this array + with the grayscale colours + + \verb+void getScreenRGB(pixel_t *output_rgb_buffer)+: This method should receive an array of length + $3 \times$ width $\times$ height (generally $3 \times 160 \times 210 = 100,800$) and then it will fill this + array with the RGB colours. The first positions contain the red colours, followed by the green colours + and then the blue colours + \verb+const ALERAM &getRAM()+: Returns a vector containing current RAM content (byte-level). \verb+void saveState()+: Saves the current state of the system if one wants to be able to recover @@ -702,6 +713,18 @@ \section{ALE Interface Specifications}\label{sec:functions} \verb+void loadState()+: Loads a previous saved state of the system once we have a state saved. + \verb+ALEState cloneState()+: Makes a copy of the environment state. This copy does \textbf{not} + include pseudo-randomness, making it suitable for planning purposes. + + \verb+ALEState cloneSystemState()+: This makes a copy of the system and environment state, + suitable for serialization. This includes pseudo-randomness and so is \textbf{not} suitable for planning + purposes. + + \verb+void restoreState(const ALEState& state)+: Reverse operation of \verb+cloneState()+. This does not + restore pseudo-randomness, so that repeated calls to \verb+restoreState()+ in the stochastic controls setting + will not lead to the same outcomes. By contrast, see \verb+restoreSystemState+. + + \verb+void restoreSystemState(const ALEState& state)+: Reverse operation of \verb+cloneSystemState+. \subsection{Recording trajectories} \indent \indent \verb+void saveScreenPNG(const string& filename)+: Saves the current screen as @@ -730,14 +753,15 @@ \subsection{Main Arguments} -game_controller -- selects an ALE interface default: unset - -random_seed <###|time> -- picks the ALE random seed, or sets it to current time - default: time + -random_seed <###> -- picks the ALE random seed; if set to 0, sets to current + time instead + default: 0 -display_screen -- if true and SDL is enabled, displays ALE screen default: false -sound -- if true and SDL is enabled, the game will have game - sounds enabled + sounds enabled default: false diff --git a/dockerfile.3.6 b/dockerfile.3.6 new file mode 100644 index 0000000..aef8fdd --- /dev/null +++ b/dockerfile.3.6 @@ -0,0 +1,10 @@ +FROM python:3.6 + +RUN apt-get -y update && \ + apt-get -y install cmake && \ + pip install pytest + +COPY . /root/code +WORKDIR /root/code + +RUN pip install . diff --git a/get_platform.py b/get_platform.py new file mode 100644 index 0000000..a8b37c3 --- /dev/null +++ b/get_platform.py @@ -0,0 +1,9 @@ +import distutils.util +platform = distutils.util.get_platform() + +# technically, our platform is not actually multilinux... so this may fail in some distros +# however, tested in python:3.6 docker image (by construction) +# and in ubuntu:16.04 +platform = platform.replace('linux', 'manylinux1') + +print(platform) diff --git a/install_osx.sh b/install_osx.sh new file mode 100755 index 0000000..579e349 --- /dev/null +++ b/install_osx.sh @@ -0,0 +1,9 @@ +set -ex +CONDA_VER=3.7.0 +curl https://repo.continuum.io/miniconda/Miniconda3-${CONDA_VER}-MacOSX-x86_64.sh -o ~/miniconda.sh +bash ~/miniconda.sh -b -p $HOME/miniconda +export PATH="$HOME/miniconda/bin:$PATH" + +conda install pip +python --version +pip --version diff --git a/installzlib.bat b/installzlib.bat new file mode 100644 index 0000000..ebf8a01 --- /dev/null +++ b/installzlib.bat @@ -0,0 +1,12 @@ +curl -O https://zlib.net/zlib1211.zip +unzip zlib1211.zip +cp -r zlib-1.2.11 atari_py/ale_interface/src/zlib +cd atari_py/ale_interface/src/zlib +cmake -DCMAKE_GENERATOR_PLATFORM=x64 . +cmake --build . + +cd ../.. +mkdir -p build +cp src/zlib/Debug/zlibstaticd.lib build/z.lib + + diff --git a/multibuild.sh b/multibuild.sh new file mode 100755 index 0000000..c5b50fa --- /dev/null +++ b/multibuild.sh @@ -0,0 +1,13 @@ +set -ex +export REPO_DIR=. +export BUILD_COMMIT=$TRAVIS_COMMIT +export PLAT=x86_64 +export MB_PYTHON_VERSION=$PY_VER + +git clone https://github.com/matthew-brett/multibuild && cd multibuild && git checkout 254ad28 && cd .. +source multibuild/common_utils.sh +source multibuild/travis_steps.sh +before_install +build_wheel $REPO_DIR $PLAT +install_run $PLAT + diff --git a/setup.py b/setup.py index 3670684..de02158 100644 --- a/setup.py +++ b/setup.py @@ -1,34 +1,99 @@ +import glob import os -from setuptools import setup -import subprocess import sys -from distutils.command.build import build as DistutilsBuild - -with open(os.path.join(os.path.dirname(__file__), 'atari_py/package_data.txt')) as f: - package_data = [line.rstrip() for line in f.readlines()] - -class Build(DistutilsBuild): - def run(self): - try: - subprocess.check_call(['make', 'build', '-C', 'atari_py/ale_interface']) - except subprocess.CalledProcessError as e: - sys.stderr.write("Could not build atari-py: %s. (HINT: are you sure cmake is installed? You might also be missing a library. Atari-py requires: zlib [installable as 'apt-get install zlib1g-dev' on Ubuntu].)\n" % e) - raise - except OSError as e: - sys.stderr.write("Unable to execute 'make build -C atari_py/ale_interface'. HINT: are you sure `make` is installed?\n") - raise - DistutilsBuild.run(self) +from setuptools import setup +from setuptools.command.build_ext import build_ext as _build_ext +from setuptools.extension import Library + + +# Force linker to produce a shared library +class build_ext(_build_ext): + if sys.platform.startswith('linux'): + def get_ext_filename(self, fullname): + import setuptools.command.build_ext + tmp = setuptools.command.build_ext.libtype + setuptools.command.build_ext.libtype = 'shared' + ret = _build_ext.get_ext_filename(self, fullname) + setuptools.command.build_ext.libtype = tmp + return ret + + def setup_shlib_compiler(self): + _build_ext.setup_shlib_compiler(self) + if sys.platform == 'win32': + from distutils.ccompiler import CCompiler + mtd = CCompiler.link_shared_object.__get__(self.shlib_compiler) + self.shlib_compiler.link_shared_object = mtd + elif sys.platform.startswith('linux'): + from functools import partial + c = self.shlib_compiler + c.link_shared_object = partial(c.link, c.SHARED_LIBRARY) + + +def list_files(path): + for root, dirs, files in os.walk(path): + for fname in files: + yield os.path.join(root, fname) + + for dirname in dirs: + for rpath in list_files(os.path.join(root, dirname)): + yield rpath + + +basepath = os.path.normpath(r'atari_py/ale_interface/src') +modules = [os.path.join(basepath, os.path.normpath(path)) + for path in 'common controllers emucore emucore/m6502/src ' + 'emucore/m6502/src/bspf/src environment games ' + 'games/supported external external/TinyMT'.split()] +defines = [] +cflags = [] +ldflags = [] +sources = [os.path.join('atari_py', 'ale_c_wrapper.cpp'), + os.path.join(basepath, 'ale_interface.cpp')] +includes = ['atari_py', basepath, os.path.join(basepath, 'os_dependent')] +includes += modules + +for folder in modules: + sources += glob.glob(os.path.join(folder, '*.c')) + sources += glob.glob(os.path.join(folder, '*.c?[xp]')) + +if sys.platform.startswith('linux'): + defines.append(('BSPF_UNIX', None)) + for fname in 'SettingsUNIX.cxx OSystemUNIX.cxx FSNodePOSIX.cxx'.split(): + sources.append(os.path.join(basepath, 'os_dependent', fname)) +elif sys.platform == "darwin": + defines.append(('BSPF_MAC_OSX', None)) + includes.append( + '/System/Library/Frameworks/vecLib.framework/Versions/Current/Headers') +elif sys.platform == "win32": + defines.append(('BSPF_WIN32', None)) + for fname in 'SettingsWin32.cxx OSystemWin32.cxx FSNodeWin32.cxx'.split(): + sources.append(os.path.join(basepath, 'os_dependent', fname)) + # disable msvc secure warnings + defines.append(('_CRT_SECURE_NO_WARNINGS', None)) + cflags += ['/O2', '/GL', '/GF', '/EHs-'] + ldflags += ['/LTCG'] + + +ale_c = Library('ale_c', + define_macros=defines, + extra_compile_args=cflags, + extra_link_args=ldflags, + sources=sources, + include_dirs=includes, + ) + setup(name='atari-py', - version='0.0.14', + version='1.2.2', description='Python bindings to Atari games', url='https://github.com/openai/atari-py', author='OpenAI', author_email='info@openai.com', license='', packages=['atari_py'], - package_data={'atari_py': package_data}, - cmdclass={'build': Build}, + package_data={'atari_py': ['atari_roms/*']}, + cmdclass={'build_ext': build_ext}, + ext_modules=[ale_c], install_requires=['numpy'], tests_require=['nose2'] -) + ) diff --git a/tox.ini b/tox.ini index 77b0842..43fc845 100644 --- a/tox.ini +++ b/tox.ini @@ -7,25 +7,19 @@ envlist = py27, py35 [testenv:py35] -whitelist_externals=make -deps = - nose2 - numpy +whitelist_externals=echo +install_command=echo {packages} commands = - make - python setup.py build + pip install numpy nose2 pip install -e . + nose2 python setup.py clean --all - make clean [testenv:py27] -whitelist_externals=make - echo +whitelist_externals=echo install_command=echo {packages} commands = pip install numpy nose2 - python setup.py build pip install -e . nose2 python setup.py clean --all - make clean diff --git a/win_build.sh b/win_build.sh new file mode 100644 index 0000000..4e62d12 --- /dev/null +++ b/win_build.sh @@ -0,0 +1,20 @@ +set -ex +choco install --yes python3 --version $PY_VER +export PYROOT=/c/Python${PY_VER//./} +export PATH=$PYROOT:$PYROOT/Scripts:$PATH +pip install cmake pytest +./installzlib.bat +mkdir -p atari_py/ale_interface/build + +cd atari_py/ale_interface/build +cmake -DCMAKE_GENERATOR_PLATFORM=x64 .. +cmake --build . +cp Debug/ale_c.dll ../ +cd ../../../ + +pip install wheel && pip wheel . -w wheelhouse --no-deps -vvv +ls wheelhouse/atari_py* +rm -rf atari_py* +pip install $(ls wheelhouse/atari_py*) +pytest --pyargs atari_py.tests +