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/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.md b/README.md index 76ae5f4..2ccf90a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ +**Status:** Maintenance (expect bug fixes and minor updates) + # atari_py +[![Build Status](https://travis-ci.org/openai/atari-py.svg?branch=master)](https://travis-ci.org/openai/atari-py) + A packaged and slightly-modified version of [https://github.com/bbitmaster/ale_python_interface](https://github.com/bbitmaster/ale_python_interface). ## Installation @@ -7,17 +11,12 @@ A packaged and slightly-modified version of [https://github.com/bbitmaster/ale_p To install via pip, run: ```pip install atari-py``` +That *should* install a correct binary verison for your OS. If that does not work (or if you would like get the latest-latest +version, or you just want to tinker with the code yourself) see next paragraph. -Alternatively, you can install using setuptools using - -```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``` +## Installation from source -### Common issues + - make sure you have `git`, `cmake` and `zlib1g` system packages installed + - clone the repo + - run `pip install -e .` -- Make sure you have `cmake` installed. On OSX, you probably want - `brew install cmake`. 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..cf01cc1 100644 --- a/atari_py/ale_c_wrapper.h +++ b/atari_py/ale_c_wrapper.h @@ -52,16 +52,28 @@ extern "C" { int getScreenWidth(ALEInterface *ale){return ale->getScreen().width();} int getScreenHeight(ALEInterface *ale){return ale->getScreen().height();} - void getScreenRGB(ALEInterface *ale, int *output_buffer){ + 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 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(); + 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; + } } void getScreenGrayscale(ALEInterface *ale, unsigned char *output_buffer){ @@ -75,32 +87,22 @@ extern "C" { void saveState(ALEInterface *ale){ale->saveState();} void loadState(ALEInterface *ale){ale->loadState();} + ALEState* cloneState(ALEInterface *ale){return new ALEState(ale->cloneState());} + void restoreState(ALEInterface *ale, ALEState* state){ale->restoreState(*state);} + ALEState* cloneSystemState(ALEInterface *ale){return new ALEState(ale->cloneSystemState());} + void restoreSystemState(ALEInterface *ale, ALEState* state){ale->restoreSystemState(*state);} + void deleteState(ALEState* state){delete state;} 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 ALEState_getFrameNumber(ALEState* state) { - return state->getFrameNumber(); - } - - int ALEState_getEpisodeFrameNumber(ALEState* state) { - return state->getEpisodeFrameNumber(); - } - - bool ALEState_equals(ALEState* a, ALEState *b) { - return a->equals(*b); - } + // 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. + void encodeState(ALEState *state, char *buf, int buf_len); + int encodeStateLen(ALEState *state); + ALEState *decodeState(const char *serialized, int len); + // 0: Info, 1: Warning, 2: Error + 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..dfeb319 100644 --- a/atari_py/ale_interface/CMakeLists.txt +++ b/atari_py/ale_interface/CMakeLists.txt @@ -1,11 +1,16 @@ cmake_minimum_required (VERSION 2.6) project(ale) +set(ALEVERSION "0.5") + 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") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -O3 -fomit-frame-pointer -D__STDC_CONSTANT_MACROS") add_definitions(-DHAVE_INTTYPES) set(LINK_LIBS z) @@ -50,14 +55,65 @@ if(APPLE) set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") endif() -if(WINDOWS OR MINGW) +if(WIN32 OR MINGW) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) list(APPEND SOURCES ${SOURCE_DIR}/os_dependent/SettingsWin32.cxx ${SOURCE_DIR}/os_dependent/OSystemWin32.cxx ${SOURCE_DIR}/os_dependent/FSNodeWin32.cxx) else() 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 +127,63 @@ 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(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 ${PROJECT_BINARY_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 ${PROJECT_BINARY_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 ${PROJECT_BINARY_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 RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_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 ${PROJECT_BINARY_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) + + # 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 RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_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) @@ -104,13 +192,50 @@ 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 RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_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 RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_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..6218316 100644 --- a/atari_py/ale_interface/src/ale_interface.cpp +++ b/atari_py/ale_interface/src/ale_interface.cpp @@ -62,8 +62,6 @@ void ALEInterface::createOSystem(std::auto_ptr &theOSystem, theSettings.reset(new SettingsUNIX(theOSystem.get())); #endif - setDefaultSettings(theOSystem->settings()); - theOSystem->settings().loadConfig(); } @@ -91,16 +89,10 @@ void ALEInterface::loadSettings(const string& romfile, 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); @@ -196,9 +188,8 @@ void ALEInterface::reset_game() { } // 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. @@ -250,7 +241,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 +250,30 @@ 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 * 3); +} + // Returns the current RAM content const ALERAM& ALEInterface::getRAM() { return environment->getRAM(); @@ -282,6 +297,14 @@ void ALEInterface::restoreState(const ALEState& state) { return environment->restoreState(state); } +ALEState ALEInterface::cloneSystemState() { + return environment->cloneSystemState(); +} + +void ALEInterface::restoreSystemState(const ALEState& state) { + return environment->restoreSystemState(state); +} + void ALEInterface::saveScreenPNG(const string& filename) { ScreenExporter exporter(theOSystem->colourPalette()); diff --git a/atari_py/ale_interface/src/ale_interface.hpp b/atari_py/ale_interface/src/ale_interface.hpp index 47a5f7a..997a3e4 100644 --- a/atari_py/ale_interface/src/ale_interface.hpp +++ b/atari_py/ale_interface/src/ale_interface.hpp @@ -37,7 +37,6 @@ #include "os_dependent/SettingsUNIX.hxx" #include "os_dependent/OSystemUNIX.hxx" #include "games/Roms.hpp" -#include "common/Defaults.hpp" #include "common/display_screen.h" #include "environment/stella_environment.hpp" #include "common/ScreenExporter.hpp" @@ -46,7 +45,7 @@ #include #include -static const std::string Version = "0.5.0"; +static const std::string Version = "0.5.1"; /** This class interfaces ALE with external code for controlling agents. @@ -83,7 +82,7 @@ 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(); @@ -103,11 +102,20 @@ class ALEInterface { const 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,10 +125,22 @@ 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); diff --git a/atari_py/ale_interface/src/common/ColourPalette.cpp b/atari_py/ale_interface/src/common/ColourPalette.cpp index 1244c52..acf91f3 100644 --- a/atari_py/ale_interface/src/common/ColourPalette.cpp +++ b/atari_py/ale_interface/src/common/ColourPalette.cpp @@ -34,7 +34,7 @@ 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 ); + uInt8 lum = (uInt8) round(r * 0.2989 + g * 0.5870 + b * 0.1140); return packRGB(lum, lum, lum); } @@ -45,8 +45,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 +58,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 +84,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 +109,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/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/module.mk b/atari_py/ale_interface/src/common/module.mk deleted file mode 100644 index 336f4fe..0000000 --- a/atari_py/ale_interface/src/common/module.mk +++ /dev/null @@ -1,18 +0,0 @@ -MODULE := src/common - -MODULE_OBJS := \ - src/common/SoundNull.o \ - src/common/SoundSDL.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 += \ - src/common - -# Include common rules -include $(srcdir)/common.rules diff --git a/atari_py/ale_interface/src/controllers/fifo_controller.cpp b/atari_py/ale_interface/src/controllers/fifo_controller.cpp index bca59c4..e3da759 100644 --- a/atari_py/ale_interface/src/controllers/fifo_controller.cpp +++ b/atari_py/ale_interface/src/controllers/fifo_controller.cpp @@ -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/controllers/module.mk b/atari_py/ale_interface/src/controllers/module.mk deleted file mode 100644 index 6363a6c..0000000 --- a/atari_py/ale_interface/src/controllers/module.mk +++ /dev/null @@ -1,12 +0,0 @@ -MODULE := src/controllers - -MODULE_OBJS := \ - src/controllers/ale_controller.o \ - src/controllers/fifo_controller.o \ - src/controllers/rlglue_controller.o \ - -MODULE_DIRS += \ - src/controllers - -# Include common rules -include $(srcdir)/common.rules 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..c619dad 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(); @@ -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/FSNode.hxx b/atari_py/ale_interface/src/emucore/FSNode.hxx index 4085651..b05f8f7 100644 --- a/atari_py/ale_interface/src/emucore/FSNode.hxx +++ b/atari_py/ale_interface/src/emucore/FSNode.hxx @@ -120,8 +120,10 @@ 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; } 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..6ef3740 100644 --- a/atari_py/ale_interface/src/emucore/OSystem.cxx +++ b/atari_py/ale_interface/src/emucore/OSystem.cxx @@ -24,8 +24,6 @@ #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,7 +50,7 @@ using namespace std; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OSystem::OSystem() : - myEvent(NULL), //ALE + myEvent(NULL), mySound(NULL), mySettings(NULL), myPropSet(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() { @@ -481,72 +485,14 @@ ALE */ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool OSystem::openROM(const string& rom, string& md5, uInt8** image, int* size) { - // 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; + // 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); - } + *image = new uInt8[MAX_ROM_SIZE]; + *size = gzread(f, *image, MAX_ROM_SIZE); + gzclose(f); // 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 diff --git a/atari_py/ale_interface/src/emucore/OSystem.hxx b/atari_py/ale_interface/src/emucore/OSystem.hxx index 1cec3d1..4eca7d6 100644 --- a/atari_py/ale_interface/src/emucore/OSystem.hxx +++ b/atari_py/ale_interface/src/emucore/OSystem.hxx @@ -343,6 +343,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 @@ -436,7 +456,8 @@ 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; diff --git a/atari_py/ale_interface/src/emucore/Random.cxx b/atari_py/ale_interface/src/emucore/Random.cxx index 2d4e74a..6f3075e 100644 --- a/atari_py/ale_interface/src/emucore/Random.cxx +++ b/atari_py/ale_interface/src/emucore/Random.cxx @@ -18,59 +18,129 @@ #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" -// 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; +// Implementation of Random's random number generator wrapper. +class Random::Impl { + typedef tinymt32_t randgen_t; - // Random number generator - randgen_t rndGenerator; -} + public: + + Impl(); -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Random::seed(uInt32 value) -{ - ourSeed = value; - ourSeeded = true; - // TODO(mgb): this is the C++11 variant. - // rndGenerator.seed(ourSeed); + // Implementations of the methods defined in Random.hpp. + void seed(uInt32 value); + uInt32 next(); + double nextDouble(); - tinymt32_init(&RandomStatic::rndGenerator, ourSeed); + 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 + //seed((uInt32) time(NULL)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Random::Random() +void Random::Impl::seed(uInt32 value) { - // If we haven't been seeded then seed ourself - if(!ourSeeded) - seed((uInt32) time(NULL)); + m_seed = value; + // TODO(mgb): this is the C++11 variant. + // rndGenerator.seed(ourSeed); + tinymt32_init(&m_randgen, m_seed); } - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Random::next() +uInt32 Random::Impl::next() { // TODO(mgb): C++11 // return rndGenerator(); - return static_cast(tinymt32_generate_uint32(&RandomStatic::rndGenerator)); + return static_cast(tinymt32_generate_uint32(&m_randgen)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -double Random::nextDouble() +double Random::Impl::nextDouble() { // TODO(mgb): C++11 // return rndGenerator() / double(rndGenerator.max() + 1.0); - return tinymt32_generate_32double(&RandomStatic::rndGenerator); + return tinymt32_generate_32double(&m_randgen); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Random::ourSeed = 0; +Random::Random() : + m_pimpl(new Random::Impl()) +{ +} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Random::ourSeeded = false; +Random::~Random() { + if (m_pimpl != NULL) { + delete m_pimpl; + m_pimpl = NULL; + } +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Random::seed(uInt32 value) +{ + m_pimpl->seed(value); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 Random::next() +{ + return m_pimpl->next(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +double Random::nextDouble() +{ + return m_pimpl->nextDouble(); +} + +Random& Random::getInstance() { + return s_random; +} + +bool Random::saveState(Serializer& ser) { + + // Serialize the TinyMT state + for (int i = 0; i < 4; i++) + ser.putInt(m_pimpl->m_randgen.status[i]); + // These aren't really needed, but we serialize them anyway + ser.putInt(m_pimpl->m_randgen.mat1); + ser.putInt(m_pimpl->m_randgen.mat2); + ser.putInt(m_pimpl->m_randgen.tmat); + return true; +} + +bool Random::loadState(Deserializer& deser) { + + // Deserialize the TinyMT state + for (int i = 0; i < 4; i++) + m_pimpl->m_randgen.status[i] = deser.getInt(); + m_pimpl->m_randgen.mat1 = deser.getInt(); + m_pimpl->m_randgen.mat2 = deser.getInt(); + m_pimpl->m_randgen.tmat = deser.getInt(); + + return true; +} diff --git a/atari_py/ale_interface/src/emucore/Random.hxx b/atari_py/ale_interface/src/emucore/Random.hxx index 1ed0518..6410983 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,29 @@ 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 + // tinymt). + 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..fe1224c 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_file", "")); + + // 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/m6502/module.mk b/atari_py/ale_interface/src/emucore/m6502/module.mk deleted file mode 100644 index 9361d5b..0000000 --- a/atari_py/ale_interface/src/emucore/m6502/module.mk +++ /dev/null @@ -1,15 +0,0 @@ -MODULE := src/emucore/m6502 - -MODULE_OBJS := \ - src/emucore/m6502/src/Device.o \ - src/emucore/m6502/src/M6502.o \ - src/emucore/m6502/src/M6502Low.o \ - src/emucore/m6502/src/M6502Hi.o \ - src/emucore/m6502/src/NullDev.o \ - src/emucore/m6502/src/System.o - -MODULE_DIRS += \ - src/emucore/m6502/src - -# Include common rules -include $(srcdir)/common.rules diff --git a/atari_py/ale_interface/src/emucore/module.mk b/atari_py/ale_interface/src/emucore/module.mk deleted file mode 100644 index 54483c2..0000000 --- a/atari_py/ale_interface/src/emucore/module.mk +++ /dev/null @@ -1,57 +0,0 @@ -MODULE := src/emucore - -MODULE_OBJS := \ - src/emucore/AtariVox.o \ - src/emucore/Booster.o \ - src/emucore/Cart2K.o \ - src/emucore/Cart3F.o \ - src/emucore/Cart3E.o \ - src/emucore/Cart4A50.o \ - src/emucore/Cart4K.o \ - src/emucore/CartAR.o \ - src/emucore/CartCV.o \ - src/emucore/Cart.o \ - src/emucore/CartDPC.o \ - src/emucore/CartE0.o \ - src/emucore/CartE7.o \ - src/emucore/CartF4.o \ - src/emucore/CartF4SC.o \ - src/emucore/CartF6.o \ - src/emucore/CartF6SC.o \ - src/emucore/CartF8.o \ - src/emucore/CartF8SC.o \ - src/emucore/CartFASC.o \ - src/emucore/CartFE.o \ - src/emucore/CartMB.o \ - src/emucore/CartMC.o \ - src/emucore/CartUA.o \ - src/emucore/Cart0840.o \ - src/emucore/Console.o \ - src/emucore/Control.o \ - src/emucore/Deserializer.o \ - src/emucore/Driving.o \ - src/emucore/Event.o \ - src/emucore/FSNode.o \ - src/emucore/Joystick.o \ - src/emucore/Keyboard.o \ - src/emucore/M6532.o \ - src/emucore/MD5.o \ - src/emucore/MediaSrc.o \ - src/emucore/OSystem.o \ - src/emucore/Paddles.o \ - src/emucore/Props.o \ - src/emucore/PropsSet.o \ - src/emucore/Random.o \ - src/emucore/Serializer.o \ - src/emucore/Settings.o \ - src/emucore/SpeakJet.o \ - src/emucore/Switches.o \ - src/emucore/TIA.o \ - src/emucore/TIASnd.o \ - src/emucore/unzip.o \ - -MODULE_DIRS += \ - src/emucore - -# Include common rules -include $(srcdir)/common.rules diff --git a/atari_py/ale_interface/src/emucore/rsynth/module.mk b/atari_py/ale_interface/src/emucore/rsynth/module.mk deleted file mode 100644 index b1ad52a..0000000 --- a/atari_py/ale_interface/src/emucore/rsynth/module.mk +++ /dev/null @@ -1,16 +0,0 @@ -MODULE := src/emucore/rsynth - -MODULE_OBJS := \ - src/emucore/rsynth/darray.o \ - src/emucore/rsynth/elements.o \ - src/emucore/rsynth/holmes.o \ - src/emucore/rsynth/opsynth.o \ - src/emucore/rsynth/phones.o \ - src/emucore/rsynth/phtoelm.o \ - src/emucore/rsynth/trie.o - -MODULE_DIRS += \ - src/emucore/rsynth - -# Include common rules -include $(srcdir)/common.rules diff --git a/atari_py/ale_interface/src/emucore/unzip.h b/atari_py/ale_interface/src/emucore/unzip.h index 2c8ae7f..755600b 100644 --- a/atari_py/ale_interface/src/emucore/unzip.h +++ b/atari_py/ale_interface/src/emucore/unzip.h @@ -41,6 +41,13 @@ #ifndef _unz_H #define _unz_H +/* Gentoo removed OF from their copy of zconf.h + * (https://bugs.gentoo.org/show_bug.cgi?id=383179) */ +/* but our copy of minizip needs it. */ +#ifndef OF +#define OF(args) args +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/atari_py/ale_interface/src/environment/ale_screen.hpp b/atari_py/ale_interface/src/environment/ale_screen.hpp index 021b293..9fe2c60 100644 --- a/atari_py/ale_interface/src/environment/ale_screen.hpp +++ b/atari_py/ale_interface/src/environment/ale_screen.hpp @@ -11,7 +11,7 @@ * ale_screen.hpp * * A class that encapsulates an Atari 2600 screen. Code is provided inline for - * efficiency reasonss. + * efficiency reasons. * **************************************************************************** */ diff --git a/atari_py/ale_interface/src/environment/ale_state.cpp b/atari_py/ale_interface/src/environment/ale_state.cpp index 566b8e3..b8fbc70 100644 --- a/atari_py/ale_interface/src/environment/ale_state.cpp +++ b/atari_py/ale_interface/src/environment/ale_state.cpp @@ -15,20 +15,14 @@ #include "../common/Constants.h" using namespace std; +#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){ -} - -ALEState::ALEState(const ALEState &rhs): - 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_episode_frame_number(0) { } ALEState::ALEState(const ALEState &rhs, std::string serialized): @@ -39,14 +33,33 @@ ALEState::ALEState(const ALEState &rhs, std::string serialized): 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_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 @@ -56,11 +69,17 @@ void ALEState::load(OSystem* osystem, RomSettings* settings, std::string md5, co m_frame_number = rhs.m_frame_number; } -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 +87,25 @@ 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.putString(this->m_serialized_state); + + return ser.get_str(); +} @@ -451,6 +482,6 @@ 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); } diff --git a/atari_py/ale_interface/src/environment/ale_state.hpp b/atari_py/ale_interface/src/environment/ale_state.hpp index a7e226c..9dee5bc 100644 --- a/atari_py/ale_interface/src/environment/ale_state.hpp +++ b/atari_py/ale_interface/src/environment/ale_state.hpp @@ -35,11 +35,12 @@ 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); + // Restores a serialized ALEState + ALEState(const std::string &serialized); + /** Resets the system to its start state. numResetSteps 'RESET' actions are taken after the * start. */ void reset(int numResetSteps = 1); @@ -65,18 +66,22 @@ class ALEState { //Get the number of frames executed this episode. const int getEpisodeFrameNumber() const { return m_episode_frame_number; } + std::string serialize(); + protected: // Let StellaEnvironment access these methods: they are needed for emulation purposes 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); diff --git a/atari_py/ale_interface/src/environment/module.mk b/atari_py/ale_interface/src/environment/module.mk deleted file mode 100644 index c35a5ab..0000000 --- a/atari_py/ale_interface/src/environment/module.mk +++ /dev/null @@ -1,12 +0,0 @@ -MODULE := src/environment - -MODULE_OBJS := \ - src/environment/ale_state.o \ - src/environment/stella_environment.o \ - src/environment/phosphor_blend.o \ - -MODULE_DIRS += \ - src/environment - -# Include common rules -include $(srcdir)/common.rules 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..83a065a 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), @@ -103,17 +103,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,32 +137,35 @@ 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(); + // Similarly record screen as needed + if (m_screen_exporter.get() != NULL) + m_screen_exporter->saveNext(m_screen); + // 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; } @@ -183,7 +189,7 @@ 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)); @@ -230,8 +236,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..9125d80 100644 --- a/atari_py/ale_interface/src/environment/stella_environment.hpp +++ b/atari_py/ale_interface/src/environment/stella_environment.hpp @@ -44,11 +44,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. @@ -58,7 +65,7 @@ class StellaEnvironment { reward_t act(Action player_a_action, Action player_b_action); /** 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 +97,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 @@ -108,7 +115,6 @@ class StellaEnvironment { 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; // The last actions taken by our players Action m_player_a_action, m_player_b_action; diff --git a/atari_py/ale_interface/src/external/TinyMT/tinymt32.c b/atari_py/ale_interface/src/external/TinyMT/tinymt32.c index ccc19f2..4f69fd3 100644 --- a/atari_py/ale_interface/src/external/TinyMT/tinymt32.c +++ b/atari_py/ale_interface/src/external/TinyMT/tinymt32.c @@ -60,6 +60,12 @@ static void period_certification(tinymt32_t * random) { * @param seed a 32-bit unsigned integer used as a seed. */ void tinymt32_init(tinymt32_t * random, uint32_t seed) { + // MGB: These values were apparently drawn from the aether. This isn't good for reproducibility. + // I will set them to some bunk values. + random->mat1 = 4753849; + random->mat2 = 3231259923; + random->tmat = 614784120; + int i; random->status[0] = seed; random->status[1] = random->mat1; 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.hpp b/atari_py/ale_interface/src/games/RomSettings.hpp index c7fc4c4..a7b096c 100644 --- a/atari_py/ale_interface/src/games/RomSettings.hpp +++ b/atari_py/ale_interface/src/games/RomSettings.hpp @@ -33,6 +33,7 @@ #ifndef __ROMSETTINGS_HPP__ #define __ROMSETTINGS_HPP__ +#include #include "../common/Constants.h" #include "../emucore/Serializer.hxx" #include "../emucore/Deserializer.hxx" diff --git a/atari_py/ale_interface/src/games/Roms.cpp b/atari_py/ale_interface/src/games/Roms.cpp index 084ca63..ad49e44 100644 --- a/atari_py/ale_interface/src/games/Roms.cpp +++ b/atari_py/ale_interface/src/games/Roms.cpp @@ -14,6 +14,7 @@ #include "RomUtils.hpp" // include the game implementations +#include "supported/Adventure.hpp" #include "supported/AirRaid.hpp" #include "supported/Alien.hpp" #include "supported/Amidar.hpp" @@ -46,6 +47,7 @@ #include "supported/IceHockey.hpp" #include "supported/JamesBond.hpp" #include "supported/JourneyEscape.hpp" +#include "supported/Kaboom.hpp" #include "supported/Kangaroo.hpp" #include "supported/Krull.hpp" #include "supported/KungFuMaster.hpp" @@ -79,6 +81,7 @@ /* list of supported games */ static const RomSettings *roms[] = { + new AdventureSettings(), new AirRaidSettings(), new AlienSettings(), new AmidarSettings(), @@ -111,6 +114,7 @@ static const RomSettings *roms[] = { new IceHockeySettings(), new JamesBondSettings(), new JourneyEscapeSettings(), + new KaboomSettings(), new KangarooSettings(), new KrullSettings(), new KungFuMasterSettings(), diff --git a/atari_py/ale_interface/src/games/Roms.hpp b/atari_py/ale_interface/src/games/Roms.hpp index 056906d..9b47b37 100644 --- a/atari_py/ale_interface/src/games/Roms.hpp +++ b/atari_py/ale_interface/src/games/Roms.hpp @@ -13,6 +13,7 @@ #define __ROMS_HPP__ #include +#include struct RomSettings; diff --git a/atari_py/ale_interface/src/games/module.mk b/atari_py/ale_interface/src/games/module.mk deleted file mode 100644 index 3af60e7..0000000 --- a/atari_py/ale_interface/src/games/module.mk +++ /dev/null @@ -1,73 +0,0 @@ -MODULE := src/games - -MODULE_OBJS := \ - src/games/Roms.o \ - src/games/RomSettings.o \ - src/games/RomUtils.o \ - src/games/supported/AirRaid.o \ - src/games/supported/Alien.o \ - src/games/supported/Amidar.o \ - src/games/supported/Assault.o \ - src/games/supported/Asterix.o \ - src/games/supported/Asteroids.o \ - src/games/supported/Atlantis.o \ - src/games/supported/BankHeist.o \ - src/games/supported/BattleZone.o \ - src/games/supported/BeamRider.o \ - src/games/supported/Berzerk.o \ - src/games/supported/Bowling.o \ - src/games/supported/Boxing.o \ - src/games/supported/Breakout.o \ - src/games/supported/Carnival.o \ - src/games/supported/Centipede.o \ - src/games/supported/ChopperCommand.o \ - src/games/supported/CrazyClimber.o \ - src/games/supported/Defender.o \ - src/games/supported/DemonAttack.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/Frostbite.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/Kangaroo.o \ - src/games/supported/Krull.o \ - src/games/supported/KungFuMaster.o \ - src/games/supported/MontezumaRevenge.o \ - src/games/supported/MsPacman.o \ - src/games/supported/NameThisGame.o \ - src/games/supported/Phoenix.o \ - src/games/supported/Pitfall.o \ - src/games/supported/Pong.o \ - src/games/supported/Pooyan.o \ - src/games/supported/PrivateEye.o \ - src/games/supported/QBert.o \ - src/games/supported/RiverRaid.o \ - src/games/supported/RoadRunner.o \ - src/games/supported/RoboTank.o \ - src/games/supported/Seaquest.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/TimePilot.o \ - src/games/supported/Tutankham.o \ - src/games/supported/UpNDown.o \ - src/games/supported/Venture.o \ - src/games/supported/VideoPinball.o \ - src/games/supported/WizardOfWor.o \ - src/games/supported/YarsRevenge.o \ - src/games/supported/Zaxxon.o \ - -MODULE_DIRS += \ - src/games - -# Include common rules -include $(srcdir)/common.rules 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..5887f43 --- /dev/null +++ b/atari_py/ale_interface/src/games/supported/Adventure.cpp @@ -0,0 +1,122 @@ +/* ***************************************************************************** + * + * 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; + + if (player_eaten) { + m_reward = -1; + } + + 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..ccfee50 --- /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___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 const 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/Asteroids.cpp b/atari_py/ale_interface/src/games/supported/Asteroids.cpp index 5fbab0d..b91cb9f 100644 --- a/atari_py/ale_interface/src/games/supported/Asteroids.cpp +++ b/atari_py/ale_interface/src/games/supported/Asteroids.cpp @@ -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); } diff --git a/atari_py/ale_interface/src/games/supported/Breakout.cpp b/atari_py/ale_interface/src/games/supported/Breakout.cpp index c7062c1..10fb51f 100644 --- a/atari_py/ale_interface/src/games/supported/Breakout.cpp +++ b/atari_py/ale_interface/src/games/supported/Breakout.cpp @@ -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; 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..cc3f502 100644 --- a/atari_py/ale_interface/src/games/supported/Kangaroo.cpp +++ b/atari_py/ale_interface/src/games/supported/Kangaroo.cpp @@ -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; diff --git a/atari_py/ale_interface/src/games/supported/QBert.cpp b/atari_py/ale_interface/src/games/supported/QBert.cpp index f144f41..8897327 100644 --- a/atari_py/ale_interface/src/games/supported/QBert.cpp +++ b/atari_py/ale_interface/src/games/supported/QBert.cpp @@ -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 diff --git a/atari_py/ale_interface/src/games/supported/WizardOfWor.cpp b/atari_py/ale_interface/src/games/supported/WizardOfWor.cpp index 47ea4b9..ca89ad5 100644 --- a/atari_py/ale_interface/src/games/supported/WizardOfWor.cpp +++ b/atari_py/ale_interface/src/games/supported/WizardOfWor.cpp @@ -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; diff --git a/atari_py/ale_interface/src/main.cpp b/atari_py/ale_interface/src/main.cpp index 6e9767b..f634b55 100644 --- a/atari_py/ale_interface/src/main.cpp +++ b/atari_py/ale_interface/src/main.cpp @@ -22,8 +22,6 @@ #include "emucore/FSNode.hxx" #include "emucore/OSystem.hxx" -#include "common/Defaults.hpp" - #if (defined(WIN32) || defined(__MINGW32__)) # include "os_dependent/SettingsWin32.hxx" # include "os_dependent/OSystemWin32.hxx" diff --git a/atari_py/ale_interface/src/module.mk b/atari_py/ale_interface/src/module.mk deleted file mode 100644 index eaf33fc..0000000 --- a/atari_py/ale_interface/src/module.mk +++ /dev/null @@ -1,11 +0,0 @@ -MODULE := src/ - -MODULE_OBJS := \ - src/main.o \ - src/ale_interface.o - -MODULE_DIRS += \ - src/ - -# Include common rules -include $(srcdir)/common.rules 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/src/os_dependent/module.mk b/atari_py/ale_interface/src/os_dependent/module.mk deleted file mode 100644 index d6adcef..0000000 --- a/atari_py/ale_interface/src/os_dependent/module.mk +++ /dev/null @@ -1,12 +0,0 @@ -MODULE := src/os_dependent - -MODULE_OBJS := \ - src/os_dependent/FSNodePOSIX.o \ - src/os_dependent/OSystemUNIX.o \ - src/os_dependent/SettingsUNIX.o \ - -MODULE_DIRS += \ - src/os_dependent - -# Include common rules -include $(srcdir)/common.rules 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..01fe094 100644 --- a/atari_py/ale_python_interface.py +++ b/atari_py/ale_python_interface.py @@ -2,14 +2,20 @@ # 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 +import six -ale_lib = cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), - 'ale_interface/build/libale_c.so')) +if os.name == 'posix': + ale_lib = cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), + 'ale_interface/build/libale_c.so')) +else: + ale_lib = cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), + 'ale_interface/build/ale_c.dll')) ale_lib.ALE_new.argtypes = None ale_lib.ALE_new.restype = c_void_p @@ -23,7 +29,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 @@ -65,76 +71,70 @@ 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 +ale_lib.saveScreenPNG.argtypes = [c_void_p, c_char_p] +ale_lib.saveScreenPNG.restype = None +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) + ale_lib.loadROM(self.obj, _as_bytes(rom_file)) def act(self, action): return ale_lib.act(self.obj, int(action)) @@ -173,7 +173,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 +193,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 +206,25 @@ 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 +255,66 @@ 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""" + return ale_lib.saveScreenPNG(self.obj, _as_bytes(filename)) 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..7ef7864 100644 --- a/atari_py/package_data.txt +++ b/atari_py/package_data.txt @@ -1,13 +1,10 @@ -ale_interface/build/*.so +ale_interface/build/libale_c.so +ale_interface/build/ale_c.dll 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 +12,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 @@ -33,14 +28,12 @@ ale_interface/src/common/VideoModeList.hxx ale_interface/src/common/display_screen.cpp ale_interface/src/common/display_screen.h ale_interface/src/common/misc_tools.h -ale_interface/src/common/module.mk ale_interface/src/common/stella.png ale_interface/src/common/stella.xpm ale_interface/src/controllers/ale_controller.cpp ale_interface/src/controllers/ale_controller.hpp ale_interface/src/controllers/fifo_controller.cpp ale_interface/src/controllers/fifo_controller.hpp -ale_interface/src/controllers/module.mk ale_interface/src/controllers/rlglue_controller.cpp ale_interface/src/controllers/rlglue_controller.hpp ale_interface/src/emucore/AtariVox.cxx @@ -141,7 +134,6 @@ ale_interface/src/emucore/TIASnd.cxx ale_interface/src/emucore/TIASnd.hxx ale_interface/src/emucore/m6502/Copyright.txt ale_interface/src/emucore/m6502/License.txt -ale_interface/src/emucore/m6502/module.mk ale_interface/src/emucore/m6502/src/Device.cxx ale_interface/src/emucore/m6502/src/Device.hxx ale_interface/src/emucore/m6502/src/M6502.cxx @@ -162,7 +154,6 @@ ale_interface/src/emucore/m6502/src/System.hxx ale_interface/src/emucore/m6502/src/bspf/Copyright.txt ale_interface/src/emucore/m6502/src/bspf/License.txt ale_interface/src/emucore/m6502/src/bspf/src/bspf.hxx -ale_interface/src/emucore/module.mk ale_interface/src/emucore/rsynth/COPYING ale_interface/src/emucore/rsynth/Elements.def ale_interface/src/emucore/rsynth/PORTING @@ -172,7 +163,6 @@ ale_interface/src/emucore/rsynth/darray.h ale_interface/src/emucore/rsynth/elements.c ale_interface/src/emucore/rsynth/holmes.c ale_interface/src/emucore/rsynth/kmap -ale_interface/src/emucore/rsynth/module.mk ale_interface/src/emucore/rsynth/opsynth.c ale_interface/src/emucore/rsynth/phfeat.h ale_interface/src/emucore/rsynth/phones.c @@ -192,7 +182,6 @@ ale_interface/src/environment/ale_ram.hpp ale_interface/src/environment/ale_screen.hpp ale_interface/src/environment/ale_state.cpp ale_interface/src/environment/ale_state.hpp -ale_interface/src/environment/module.mk ale_interface/src/environment/phosphor_blend.cpp ale_interface/src/environment/phosphor_blend.hpp ale_interface/src/environment/stella_environment.cpp @@ -200,14 +189,14 @@ ale_interface/src/environment/stella_environment.hpp ale_interface/src/external/TinyMT/LICENSE.txt ale_interface/src/external/TinyMT/tinymt32.c ale_interface/src/external/TinyMT/tinymt32.h -ale_interface/src/external/module.mk ale_interface/src/games/RomSettings.cpp ale_interface/src/games/RomSettings.hpp ale_interface/src/games/RomUtils.cpp 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 @@ -272,6 +261,8 @@ 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/Krull.cpp @@ -331,7 +322,6 @@ ale_interface/src/games/supported/YarsRevenge.hpp ale_interface/src/games/supported/Zaxxon.cpp ale_interface/src/games/supported/Zaxxon.hpp ale_interface/src/main.cpp -ale_interface/src/module.mk ale_interface/src/os_dependent/FSNodePOSIX.cxx ale_interface/src/os_dependent/FSNodeWin32.cxx ale_interface/src/os_dependent/OSystemUNIX.cxx @@ -342,9 +332,8 @@ ale_interface/src/os_dependent/SettingsUNIX.cxx 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 +362,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 +397,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..9c19adf --- /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[atari] + 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..6e3f8ea 100644 --- a/setup.py +++ b/setup.py @@ -1,26 +1,39 @@ +import multiprocessing import os -from setuptools import setup +from setuptools import setup, Extension +from setuptools.command.build_ext import build_ext 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: +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): + +class Build(build_ext): def run(self): + if os.name != 'posix' and not self.inplace: + # silly patch to disable build steps on windows, as we are doing compilation externally + return + cores_to_use = max(1, multiprocessing.cpu_count() - 1) try: - subprocess.check_call(['make', 'build', '-C', 'atari_py/ale_interface']) + cwd = os.path.join('' if self.inplace else self.build_lib, 'atari_py', 'ale_interface', 'build') + os.makedirs(cwd, exist_ok=True) + subprocess.check_call(['cmake', '..'], cwd=cwd) + subprocess.check_call(['cmake', '--build', '.'], cwd=cwd) 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") + sys.stderr.write("Unable to execute '{}'. HINT: are you sure `make` is installed?\n".format(' '.join(cmd))) raise - DistutilsBuild.run(self) + +class CMakeExtension(Extension): + def __init__(self, name, sourcedir=''): + Extension.__init__(self, name, sources=[]) + self.sourcedir = os.path.abspath(sourcedir) setup(name='atari-py', - version='0.0.14', + version='0.1.15', description='Python bindings to Atari games', url='https://github.com/openai/atari-py', author='OpenAI', @@ -28,7 +41,9 @@ def run(self): license='', packages=['atari_py'], package_data={'atari_py': package_data}, - cmdclass={'build': Build}, - install_requires=['numpy'], + include_package_data=True, + ext_modules=[CMakeExtension('atari_py')], + cmdclass={'build_ext': Build}, + install_requires=['numpy', 'six'], tests_require=['nose2'] ) diff --git a/tox.ini b/tox.ini index 77b0842..fe17214 100644 --- a/tox.ini +++ b/tox.ini @@ -8,13 +8,14 @@ envlist = py27, py35 [testenv:py35] whitelist_externals=make -deps = - nose2 - numpy + echo +install_command=echo {packages} commands = + pip install numpy nose2 make 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..493e7bc --- /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 +