diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf2bd4e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +*__pycache__* +MAMEToolkit/emulator/mame/roms/sfiii3n.zip +MAMEToolkit/emulator/mame/mame \ No newline at end of file diff --git a/MAMEToolkit/__init__.py b/MAMEToolkit/__init__.py new file mode 100644 index 0000000..3187fa7 --- /dev/null +++ b/MAMEToolkit/__init__.py @@ -0,0 +1,2 @@ +from MAMEToolkit import emulator +from MAMEToolkit import sf_environment \ No newline at end of file diff --git a/src/emulator/Action.py b/MAMEToolkit/emulator/Action.py similarity index 100% rename from src/emulator/Action.py rename to MAMEToolkit/emulator/Action.py diff --git a/src/emulator/pipes/Address.py b/MAMEToolkit/emulator/Address.py similarity index 100% rename from src/emulator/pipes/Address.py rename to MAMEToolkit/emulator/Address.py diff --git a/src/emulator/Console.py b/MAMEToolkit/emulator/Console.py similarity index 91% rename from src/emulator/Console.py rename to MAMEToolkit/emulator/Console.py index a491f91..e0420bb 100755 --- a/src/emulator/Console.py +++ b/MAMEToolkit/emulator/Console.py @@ -1,6 +1,7 @@ import os +from pathlib import Path from subprocess import Popen, PIPE -from src.emulator.StreamGobbler import StreamGobbler +from MAMEToolkit.emulator.StreamGobbler import StreamGobbler import queue import logging @@ -13,10 +14,10 @@ class Console(object): # render is for displaying the frames to the emulator window, disabling it has little to no effect # throttle enabled will run any game at the intended gameplay speed, disabling it will run the game as fast as the computer can handle # debug enabled will print everything that comes out of the Lua engine console - def __init__(self, game_id, render=True, throttle=False, debug=False): + def __init__(self, roms_path, game_id, render=True, throttle=False, debug=False): self.logger = logging.getLogger("Console") - command = "exec ./mame -rompath roms -pluginspath plugins -skip_gameinfo -sound none -console "+game_id + command = f"exec ./mame -rompath '{str(Path(roms_path).absolute())}' -pluginspath plugins -skip_gameinfo -sound none -console "+game_id if not render: command += " -video none" if throttle: diff --git a/src/emulator/Emulator.py b/MAMEToolkit/emulator/Emulator.py similarity index 91% rename from src/emulator/Emulator.py rename to MAMEToolkit/emulator/Emulator.py index 3568232..fca4ee2 100755 --- a/src/emulator/Emulator.py +++ b/MAMEToolkit/emulator/Emulator.py @@ -1,8 +1,8 @@ import atexit import os -from src.emulator.Console import Console -from src.emulator.pipes.Pipe import Pipe -from src.emulator.pipes.DataPipe import DataPipe +from MAMEToolkit.emulator.Console import Console +from MAMEToolkit.emulator.pipes.Pipe import Pipe +from MAMEToolkit.emulator.pipes.DataPipe import DataPipe # Converts a list of action Enums into the relevant Lua engine representation @@ -11,8 +11,8 @@ def actions_to_string(actions): return '+'.join(action_strings) -def list_actions(game_id): - console = Console(game_id) +def list_actions(roms_path, game_id): + console = Console(roms_path, game_id) console.writeln('iop = manager:machine():ioport()') actions = [] ports = console.writeln("for k,v in pairs(iop.ports) do print(k) end", expect_output=True, timeout=0.5) @@ -27,17 +27,17 @@ def list_actions(game_id): # An interface for using the Lua engine console functionality class Emulator(object): - # env_id - the unique id of the emulator + # env_id - the unique id of the emulator, used for fifo pipes # game_id - the game id being used # memory_addresses - The internal memory addresses of the game which this class will return the value of at every time step # frame_ratio - the ratio of frames that will be returned, 3 means 1 out of every 3 frames will be returned. Note that his also effects how often memory addresses are read and actions are sent # See console for render, throttle & debug - def __init__(self, env_id, game_id, memory_addresses, frame_ratio=3, render=True, throttle=False, debug=False): + def __init__(self, env_id, roms_path, game_id, memory_addresses, frame_ratio=3, render=True, throttle=False, debug=False): self.memoryAddresses = memory_addresses self.frameRatio = frame_ratio # setup lua engine - self.console = Console(game_id, render=render, throttle=throttle, debug=debug) + self.console = Console(roms_path, game_id, render=render, throttle=throttle, debug=debug) atexit.register(self.close) self.wait_for_resource_registration() self.create_lua_variables() diff --git a/src/emulator/StreamGobbler.py b/MAMEToolkit/emulator/StreamGobbler.py similarity index 100% rename from src/emulator/StreamGobbler.py rename to MAMEToolkit/emulator/StreamGobbler.py diff --git a/MAMEToolkit/emulator/__init__.py b/MAMEToolkit/emulator/__init__.py new file mode 100644 index 0000000..1223f1e --- /dev/null +++ b/MAMEToolkit/emulator/__init__.py @@ -0,0 +1,3 @@ +from MAMEToolkit.emulator.Action import Action +from MAMEToolkit.emulator.Emulator import Emulator, list_actions +from MAMEToolkit.emulator.Address import Address \ No newline at end of file diff --git a/src/emulator/mame/history/history.db b/MAMEToolkit/emulator/mame/history/history.db similarity index 100% rename from src/emulator/mame/history/history.db rename to MAMEToolkit/emulator/mame/history/history.db diff --git a/src/emulator/mame/plugins/README.md b/MAMEToolkit/emulator/mame/plugins/README.md similarity index 100% rename from src/emulator/mame/plugins/README.md rename to MAMEToolkit/emulator/mame/plugins/README.md diff --git a/src/emulator/mame/plugins/boot.lua b/MAMEToolkit/emulator/mame/plugins/boot.lua similarity index 100% rename from src/emulator/mame/plugins/boot.lua rename to MAMEToolkit/emulator/mame/plugins/boot.lua diff --git a/src/emulator/mame/plugins/cheat/cheat_json.lua b/MAMEToolkit/emulator/mame/plugins/cheat/cheat_json.lua similarity index 100% rename from src/emulator/mame/plugins/cheat/cheat_json.lua rename to MAMEToolkit/emulator/mame/plugins/cheat/cheat_json.lua diff --git a/src/emulator/mame/plugins/cheat/cheat_simple.lua b/MAMEToolkit/emulator/mame/plugins/cheat/cheat_simple.lua similarity index 100% rename from src/emulator/mame/plugins/cheat/cheat_simple.lua rename to MAMEToolkit/emulator/mame/plugins/cheat/cheat_simple.lua diff --git a/src/emulator/mame/plugins/cheat/cheat_xml.lua b/MAMEToolkit/emulator/mame/plugins/cheat/cheat_xml.lua similarity index 100% rename from src/emulator/mame/plugins/cheat/cheat_xml.lua rename to MAMEToolkit/emulator/mame/plugins/cheat/cheat_xml.lua diff --git a/src/emulator/mame/plugins/cheat/init.lua b/MAMEToolkit/emulator/mame/plugins/cheat/init.lua similarity index 100% rename from src/emulator/mame/plugins/cheat/init.lua rename to MAMEToolkit/emulator/mame/plugins/cheat/init.lua diff --git a/src/emulator/mame/plugins/cheat/plugin.json b/MAMEToolkit/emulator/mame/plugins/cheat/plugin.json similarity index 100% rename from src/emulator/mame/plugins/cheat/plugin.json rename to MAMEToolkit/emulator/mame/plugins/cheat/plugin.json diff --git a/src/emulator/mame/plugins/cheat/xml_to_json.lua b/MAMEToolkit/emulator/mame/plugins/cheat/xml_to_json.lua similarity index 100% rename from src/emulator/mame/plugins/cheat/xml_to_json.lua rename to MAMEToolkit/emulator/mame/plugins/cheat/xml_to_json.lua diff --git a/src/emulator/mame/plugins/cheatfind/init.lua b/MAMEToolkit/emulator/mame/plugins/cheatfind/init.lua similarity index 100% rename from src/emulator/mame/plugins/cheatfind/init.lua rename to MAMEToolkit/emulator/mame/plugins/cheatfind/init.lua diff --git a/src/emulator/mame/plugins/cheatfind/plugin.json b/MAMEToolkit/emulator/mame/plugins/cheatfind/plugin.json similarity index 100% rename from src/emulator/mame/plugins/cheatfind/plugin.json rename to MAMEToolkit/emulator/mame/plugins/cheatfind/plugin.json diff --git a/src/emulator/mame/plugins/console/init.lua b/MAMEToolkit/emulator/mame/plugins/console/init.lua similarity index 100% rename from src/emulator/mame/plugins/console/init.lua rename to MAMEToolkit/emulator/mame/plugins/console/init.lua diff --git a/src/emulator/mame/plugins/console/plugin.json b/MAMEToolkit/emulator/mame/plugins/console/plugin.json similarity index 100% rename from src/emulator/mame/plugins/console/plugin.json rename to MAMEToolkit/emulator/mame/plugins/console/plugin.json diff --git a/src/emulator/mame/plugins/data/button_char.lua b/MAMEToolkit/emulator/mame/plugins/data/button_char.lua similarity index 100% rename from src/emulator/mame/plugins/data/button_char.lua rename to MAMEToolkit/emulator/mame/plugins/data/button_char.lua diff --git a/src/emulator/mame/plugins/data/data_command.lua b/MAMEToolkit/emulator/mame/plugins/data/data_command.lua similarity index 100% rename from src/emulator/mame/plugins/data/data_command.lua rename to MAMEToolkit/emulator/mame/plugins/data/data_command.lua diff --git a/src/emulator/mame/plugins/data/data_gameinit.lua b/MAMEToolkit/emulator/mame/plugins/data/data_gameinit.lua similarity index 100% rename from src/emulator/mame/plugins/data/data_gameinit.lua rename to MAMEToolkit/emulator/mame/plugins/data/data_gameinit.lua diff --git a/src/emulator/mame/plugins/data/data_hiscore.lua b/MAMEToolkit/emulator/mame/plugins/data/data_hiscore.lua similarity index 100% rename from src/emulator/mame/plugins/data/data_hiscore.lua rename to MAMEToolkit/emulator/mame/plugins/data/data_hiscore.lua diff --git a/src/emulator/mame/plugins/data/data_history.lua b/MAMEToolkit/emulator/mame/plugins/data/data_history.lua similarity index 100% rename from src/emulator/mame/plugins/data/data_history.lua rename to MAMEToolkit/emulator/mame/plugins/data/data_history.lua diff --git a/src/emulator/mame/plugins/data/data_mameinfo.lua b/MAMEToolkit/emulator/mame/plugins/data/data_mameinfo.lua similarity index 100% rename from src/emulator/mame/plugins/data/data_mameinfo.lua rename to MAMEToolkit/emulator/mame/plugins/data/data_mameinfo.lua diff --git a/src/emulator/mame/plugins/data/data_marp.lua b/MAMEToolkit/emulator/mame/plugins/data/data_marp.lua similarity index 100% rename from src/emulator/mame/plugins/data/data_marp.lua rename to MAMEToolkit/emulator/mame/plugins/data/data_marp.lua diff --git a/src/emulator/mame/plugins/data/data_messinfo.lua b/MAMEToolkit/emulator/mame/plugins/data/data_messinfo.lua similarity index 100% rename from src/emulator/mame/plugins/data/data_messinfo.lua rename to MAMEToolkit/emulator/mame/plugins/data/data_messinfo.lua diff --git a/src/emulator/mame/plugins/data/data_story.lua b/MAMEToolkit/emulator/mame/plugins/data/data_story.lua similarity index 100% rename from src/emulator/mame/plugins/data/data_story.lua rename to MAMEToolkit/emulator/mame/plugins/data/data_story.lua diff --git a/src/emulator/mame/plugins/data/data_sysinfo.lua b/MAMEToolkit/emulator/mame/plugins/data/data_sysinfo.lua similarity index 100% rename from src/emulator/mame/plugins/data/data_sysinfo.lua rename to MAMEToolkit/emulator/mame/plugins/data/data_sysinfo.lua diff --git a/src/emulator/mame/plugins/data/database.lua b/MAMEToolkit/emulator/mame/plugins/data/database.lua similarity index 100% rename from src/emulator/mame/plugins/data/database.lua rename to MAMEToolkit/emulator/mame/plugins/data/database.lua diff --git a/src/emulator/mame/plugins/data/init.lua b/MAMEToolkit/emulator/mame/plugins/data/init.lua similarity index 100% rename from src/emulator/mame/plugins/data/init.lua rename to MAMEToolkit/emulator/mame/plugins/data/init.lua diff --git a/src/emulator/mame/plugins/data/load_dat.lua b/MAMEToolkit/emulator/mame/plugins/data/load_dat.lua similarity index 100% rename from src/emulator/mame/plugins/data/load_dat.lua rename to MAMEToolkit/emulator/mame/plugins/data/load_dat.lua diff --git a/src/emulator/mame/plugins/data/plugin.json b/MAMEToolkit/emulator/mame/plugins/data/plugin.json similarity index 100% rename from src/emulator/mame/plugins/data/plugin.json rename to MAMEToolkit/emulator/mame/plugins/data/plugin.json diff --git a/src/emulator/mame/plugins/dummy/init.lua b/MAMEToolkit/emulator/mame/plugins/dummy/init.lua similarity index 100% rename from src/emulator/mame/plugins/dummy/init.lua rename to MAMEToolkit/emulator/mame/plugins/dummy/init.lua diff --git a/src/emulator/mame/plugins/dummy/plugin.json b/MAMEToolkit/emulator/mame/plugins/dummy/plugin.json similarity index 100% rename from src/emulator/mame/plugins/dummy/plugin.json rename to MAMEToolkit/emulator/mame/plugins/dummy/plugin.json diff --git a/src/emulator/mame/plugins/gdbstub/init.lua b/MAMEToolkit/emulator/mame/plugins/gdbstub/init.lua similarity index 100% rename from src/emulator/mame/plugins/gdbstub/init.lua rename to MAMEToolkit/emulator/mame/plugins/gdbstub/init.lua diff --git a/src/emulator/mame/plugins/gdbstub/plugin.json b/MAMEToolkit/emulator/mame/plugins/gdbstub/plugin.json similarity index 100% rename from src/emulator/mame/plugins/gdbstub/plugin.json rename to MAMEToolkit/emulator/mame/plugins/gdbstub/plugin.json diff --git a/src/emulator/mame/plugins/hiscore/hiscore.dat b/MAMEToolkit/emulator/mame/plugins/hiscore/hiscore.dat similarity index 100% rename from src/emulator/mame/plugins/hiscore/hiscore.dat rename to MAMEToolkit/emulator/mame/plugins/hiscore/hiscore.dat diff --git a/src/emulator/mame/plugins/hiscore/init.lua b/MAMEToolkit/emulator/mame/plugins/hiscore/init.lua similarity index 100% rename from src/emulator/mame/plugins/hiscore/init.lua rename to MAMEToolkit/emulator/mame/plugins/hiscore/init.lua diff --git a/src/emulator/mame/plugins/hiscore/plugin.json b/MAMEToolkit/emulator/mame/plugins/hiscore/plugin.json similarity index 100% rename from src/emulator/mame/plugins/hiscore/plugin.json rename to MAMEToolkit/emulator/mame/plugins/hiscore/plugin.json diff --git a/src/emulator/mame/plugins/hiscore/sort_hiscore.lua b/MAMEToolkit/emulator/mame/plugins/hiscore/sort_hiscore.lua similarity index 100% rename from src/emulator/mame/plugins/hiscore/sort_hiscore.lua rename to MAMEToolkit/emulator/mame/plugins/hiscore/sort_hiscore.lua diff --git a/src/emulator/mame/plugins/json/LICENSE b/MAMEToolkit/emulator/mame/plugins/json/LICENSE similarity index 100% rename from src/emulator/mame/plugins/json/LICENSE rename to MAMEToolkit/emulator/mame/plugins/json/LICENSE diff --git a/src/emulator/mame/plugins/json/README.md b/MAMEToolkit/emulator/mame/plugins/json/README.md similarity index 100% rename from src/emulator/mame/plugins/json/README.md rename to MAMEToolkit/emulator/mame/plugins/json/README.md diff --git a/src/emulator/mame/plugins/json/init.lua b/MAMEToolkit/emulator/mame/plugins/json/init.lua similarity index 100% rename from src/emulator/mame/plugins/json/init.lua rename to MAMEToolkit/emulator/mame/plugins/json/init.lua diff --git a/src/emulator/mame/plugins/json/plugin.json b/MAMEToolkit/emulator/mame/plugins/json/plugin.json similarity index 100% rename from src/emulator/mame/plugins/json/plugin.json rename to MAMEToolkit/emulator/mame/plugins/json/plugin.json diff --git a/src/emulator/mame/plugins/layout/init.lua b/MAMEToolkit/emulator/mame/plugins/layout/init.lua similarity index 100% rename from src/emulator/mame/plugins/layout/init.lua rename to MAMEToolkit/emulator/mame/plugins/layout/init.lua diff --git a/src/emulator/mame/plugins/layout/plugin.json b/MAMEToolkit/emulator/mame/plugins/layout/plugin.json similarity index 100% rename from src/emulator/mame/plugins/layout/plugin.json rename to MAMEToolkit/emulator/mame/plugins/layout/plugin.json diff --git a/src/emulator/mame/plugins/portname/init.lua b/MAMEToolkit/emulator/mame/plugins/portname/init.lua similarity index 100% rename from src/emulator/mame/plugins/portname/init.lua rename to MAMEToolkit/emulator/mame/plugins/portname/init.lua diff --git a/src/emulator/mame/plugins/portname/plugin.json b/MAMEToolkit/emulator/mame/plugins/portname/plugin.json similarity index 100% rename from src/emulator/mame/plugins/portname/plugin.json rename to MAMEToolkit/emulator/mame/plugins/portname/plugin.json diff --git a/src/emulator/mame/plugins/timer/init.lua b/MAMEToolkit/emulator/mame/plugins/timer/init.lua similarity index 100% rename from src/emulator/mame/plugins/timer/init.lua rename to MAMEToolkit/emulator/mame/plugins/timer/init.lua diff --git a/src/emulator/mame/plugins/timer/plugin.json b/MAMEToolkit/emulator/mame/plugins/timer/plugin.json similarity index 100% rename from src/emulator/mame/plugins/timer/plugin.json rename to MAMEToolkit/emulator/mame/plugins/timer/plugin.json diff --git a/src/emulator/pipes/DataPipe.py b/MAMEToolkit/emulator/pipes/DataPipe.py similarity index 96% rename from src/emulator/pipes/DataPipe.py rename to MAMEToolkit/emulator/pipes/DataPipe.py index 422e890..699f49d 100755 --- a/src/emulator/pipes/DataPipe.py +++ b/MAMEToolkit/emulator/pipes/DataPipe.py @@ -1,5 +1,5 @@ import numpy as np -from src.emulator.pipes.Pipe import Pipe +from MAMEToolkit.emulator.pipes.Pipe import Pipe # A special implementation of a Linux FIFO pipe which is used for reading all of the frame data and memory address values from the emulator diff --git a/src/emulator/pipes/Pipe.py b/MAMEToolkit/emulator/pipes/Pipe.py similarity index 92% rename from src/emulator/pipes/Pipe.py rename to MAMEToolkit/emulator/pipes/Pipe.py index da63c98..45130cb 100755 --- a/src/emulator/pipes/Pipe.py +++ b/MAMEToolkit/emulator/pipes/Pipe.py @@ -1,5 +1,5 @@ import os -from src.emulator.StreamGobbler import StreamGobbler +from MAMEToolkit.emulator.StreamGobbler import StreamGobbler from pathlib import Path from queue import Queue from threading import Thread @@ -25,11 +25,13 @@ class Pipe(object): def __init__(self, env_id, pipe_id, mode, pipes_path): self.pipeId = pipe_id + "Pipe" self.mode = mode - self.pipes_path = pipes_path - self.path = Path(pipes_path + "/" + pipe_id + "-" + str(env_id) + ".pipe") + self.pipes_path = Path(pipes_path) + if not self.pipes_path.exists(): + self.pipes_path.mkdir() + self.path = self.pipes_path.joinpath(Path(pipe_id + "-" + str(env_id) + ".pipe")) self.logger = logging.getLogger("Pipe: "+str(self.path.absolute())) if self.path.exists(): - os.remove(str(self.path.absolute())) + self.path.unlink() os.mkfifo(str(self.path.absolute())) self.logger.info("Created pipe file") diff --git a/src/sf_environment/Actions.py b/MAMEToolkit/sf_environment/Actions.py similarity index 96% rename from src/sf_environment/Actions.py rename to MAMEToolkit/sf_environment/Actions.py index 334f253..401f449 100755 --- a/src/sf_environment/Actions.py +++ b/MAMEToolkit/sf_environment/Actions.py @@ -1,5 +1,5 @@ from enum import Enum -from src.emulator.Action import Action +from MAMEToolkit.emulator.Action import Action # An enumerable class used to specify which actions can be used to interact with a game diff --git a/src/sf_environment/Environment.py b/MAMEToolkit/sf_environment/Environment.py similarity index 92% rename from src/sf_environment/Environment.py rename to MAMEToolkit/sf_environment/Environment.py index b09d3aa..8a021f1 100644 --- a/src/sf_environment/Environment.py +++ b/MAMEToolkit/sf_environment/Environment.py @@ -1,7 +1,7 @@ -from src.emulator.Emulator import Emulator -from src.emulator.pipes.Address import Address -from src.sf_environment.Steps import * -from src.sf_environment.Actions import Actions +from MAMEToolkit.emulator.Emulator import Emulator +from MAMEToolkit.emulator.Address import Address +from MAMEToolkit.sf_environment.Steps import * +from MAMEToolkit.sf_environment.Actions import Actions # Combines the data of multiple time steps @@ -58,15 +58,16 @@ def index_to_attack_action(action): # The Street Fighter specific interface for training an agent against the game class Environment(object): + # env_id - the unique identifier of the emulator environment, used to create fifo pipes # difficulty - the difficult to be used in story mode gameplay - # frameRatio, framesPerStep - see emulator class + # frame_ratio, frames_per_step - see Emulator class # render, throttle, debug - see Console class - def __init__(self, env_id, difficulty=3, frame_ratio=2, frames_per_step=3, render=True, throttle=False, debug=False): + def __init__(self, env_id, roms_path, difficulty=3, frame_ratio=3, frames_per_step=3, render=True, throttle=False, debug=False): self.difficulty = difficulty self.frame_ratio = frame_ratio self.frames_per_step = frames_per_step self.throttle = throttle - self.emu = Emulator(env_id, "sfiii3n", setup_memory_addresses(), frame_ratio=frame_ratio, render=render, throttle=throttle, debug=debug) + self.emu = Emulator(env_id, roms_path, "sfiii3n", setup_memory_addresses(), frame_ratio=frame_ratio, render=render, throttle=throttle, debug=debug) self.started = False self.expected_health = {"P1": 0, "P2": 0} self.expected_wins = {"P1": 0, "P2": 0} diff --git a/src/sf_environment/Steps.py b/MAMEToolkit/sf_environment/Steps.py similarity index 98% rename from src/sf_environment/Steps.py rename to MAMEToolkit/sf_environment/Steps.py index 64f462e..df31e68 100644 --- a/src/sf_environment/Steps.py +++ b/MAMEToolkit/sf_environment/Steps.py @@ -1,4 +1,4 @@ -from src.sf_environment.Actions import Actions +from MAMEToolkit.sf_environment.Actions import Actions # A = Agent # C = Computer diff --git a/MAMEToolkit/sf_environment/__init__.py b/MAMEToolkit/sf_environment/__init__.py new file mode 100644 index 0000000..d16c3e6 --- /dev/null +++ b/MAMEToolkit/sf_environment/__init__.py @@ -0,0 +1,3 @@ +from MAMEToolkit.sf_environment.Environment import Environment +from MAMEToolkit.sf_environment import Steps +from MAMEToolkit.sf_environment.Actions import Actions \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..15b93ab --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include MAMEToolkit/emulator/pipes/* +include MAMEToolkit/emulator/mame/mame +recursive-include MAMEToolkit/emulator/mame/plugins/ * \ No newline at end of file diff --git a/README.md b/README.md index b730f72..63d76b9 100644 --- a/README.md +++ b/README.md @@ -3,20 +3,31 @@ ## About This Python library has the to potential to train your reinforcement learning algorithm on almost any arcade game. It is currently available on Linux systems and works as a wrapper around [MAME](http://mamedev.org/). The toolkit allows your algorithm to step through gameplay while recieving the frame data and internal memory address values for tracking the games state, along with sending actions to interact with the game. +## Installation +You can use `pip` to install the library, just run: +```bash +pip install MAMEToolkit +``` + +**DISCLAIMER: We are unable to provide you with any game ROMs. It is the users own legal responsibility to acquire a game ROM for emulation. This library should only be used for non-commercial research purposes.** + ## Street Fighter Random Agent Demo -The toolkit has currently been applied to Street Fighter III Third Strike: Fight for the Future, but can modified for any game available on MAME. The following demonstrates how a random agent can be written for a street fighter environment. +The toolkit has currently been applied to Street Fighter III Third Strike: Fight for the Future (Japan 990608, NO CD), but can modified for any game available on MAME. The following demonstrates how a random agent can be written for a street fighter environment. ```python import random -from sf_environment.Environment import Environment +from MAMEToolkit.sf_environment import Environment -env = Environment(difficulty=3, frame_ratio=3, frames_per_step=3) +roms_path = "roms/" +env = Environment("env1", roms_path) env.start() while True: move_action = random.randint(0, 8) attack_action = random.randint(0, 9) - frames, reward, round_done, stage_done = env.step(move_action, attack_action) - if stage_done: - env.next_game() + frames, reward, round_done, stage_done, game_done = env.step(move_action, attack_action) + if game_done: + env.new_game() + elif stage_done: + env.next_stage() elif round_done: env.next_round() ``` @@ -25,7 +36,7 @@ The toolkit also supports hogwild training: ```Python from threading import Thread import random -from src.sf_environment.Environment import Environment +from MAMEToolkit.sf_environment import Environment def run_env(env): @@ -33,9 +44,11 @@ def run_env(env): while True: move_action = random.randint(0, 8) attack_action = random.randint(0, 9) - frames, reward, round_done, stage_done = env.step(move_action, attack_action) - if stage_done: - env.next_game() + frames, reward, round_done, stage_done, game_done = env.step(move_action, attack_action) + if game_done: + env.new_game() + elif stage_done: + env.next_stage() elif round_done: env.next_round() @@ -43,7 +56,8 @@ def run_env(env): def main(): workers = 8 # Environments must be created outside of the threads - envs = [Environment(difficulty=5, frame_ratio=3, frames_per_step=3) for i in range(workers)] + roms_path = "roms/" + envs = [Environment(f"env{i}", roms_path) for i in range(workers)] threads = [Thread(target=run_env, args=(envs[i], )) for i in range(workers)] [thread.start() for thread in threads] ``` @@ -53,11 +67,23 @@ def main(): ## Setting Up Your Own Game Environment It doesn't take much to interact with the emulator itself using the toolkit, however the challenge comes from finding the memory address values associated with the internal state you care about, and tracking said state with your environment class. The internal memory states of a game can be tracked using the [MAME Cheat Debugger](http://docs.mamedev.org/debugger/cheats.html), which allows you to track how the memory address values of the game change over time. -To create an emulation of the game you must first have the ROM for the game you are emulating and know the game ID used by MAME, for example for this version of street fighter it is 'sfiii3n'. Once you have these and have determined the memory addresses you wish to track you can start the emulation: +To create an emulation of the game you must first have the ROM for the game you are emulating and know the game ID used by MAME, for example for this version of street fighter it is 'sfiii3n'. + +**Game ID's**
+The id of your game can be found by running: ```python -from emulator.Emulator import Emulator -from emulator.pipes.Address import Address +from MAMEToolkit.emulator import Emulator +emulator = Emulator("env1", "", "", memory_addresses) +``` +This will bring up the MAME emulator. You can search through the list of games to find the one you want. The id of the game is always in brackets at the end of the game title. + +**Memory Addresses**
+Once you have these and have determined the memory addresses you wish to track you can start the emulation: +```python +from MAMEToolkit.emulator import Emulator +from MAMEToolkit.emulator import Address +roms_path = "roms/" game_id = "sfiii3n" memory_addresses = { "fighting": Address('0x0200EE44', 'u8'), @@ -67,9 +93,12 @@ memory_addresses = { "healthP2": Address('0x020691A3', 's8') } -emulator = Emulator("sfiii3n", memory_addresses) +emulator = Emulator("env1", roms_path, "sfiii3n", memory_addresses) ``` -This will immediately start the emulation and halt it when it toolkit has linked to the emulator process. Once the toolkit is linked, you can step the emulator along using the step function: +This will immediately start the emulation and halt it when it toolkit has linked to the emulator process. + +**Stepping the emulator**
+Once the toolkit is linked, you can step the emulator along using the step function: ```python data = emulator.step([]) @@ -82,19 +111,21 @@ player2_health = data["healthP2"] ``` The step function returns the frame data as a NumPy matrix, along with all of the memory address integer values from that timestep. +**Sending inputs** To send actions to the emulator you also need to determine which input ports and fields the game supports. For example, with street fighter to insert a coin the following code is required: ```python -from emulator.Action import Action +from MAMEToolkit.emulator import Action insert_coin = Action(':INPUTS', 'Coin 1') data = emulator.step([insert_coin]) ``` To identify which ports are availble use the list actions command: ```python -from emulator.Emulator import list_actions +from MAMEToolkit.emulator import list_actions +roms_path = "roms/" game_id = "sfiii3n" -print(list_actions(game_id)) +print(list_actions(roms_path, game_id)) ``` which for street fighter returns the list with all the ports and fields available for sending actions to the step function: ```python @@ -134,14 +165,14 @@ There is also the problem of transitioning games between non-learnable gameplay The emulator class also has a frame_ratio argument which can be used for adjusting the frame rate seen by your algorithm. By default MAME generates frames at 60 frames per second, however, this may be too many frames for your algorithm. The toolkit by default will use a frame_ratio of 3, which means that 1 in 3 frames are sent through the toolkit, this converts the frame rate to 20 frames per second. Using a higher frame_ratio also increases the performance of the toolkit. ```Python -from emulator.Emulator import Emulator +from MAMEToolkit.emulator import Emulator -emulator = Emulator("sfiii3n", memory_addresses, frame_ratio=3) +emulator = Emulator(roms_path, game_id, memory_addresses, frame_ratio=3) ``` ## Library Performance Benchmarks with PC Specs The development and testing of this toolkit have been completed on an 8-core AMD FX-8300 3.3GHz CPU along with a 3GB GeForce GTX 1060 GPU. -With a single random agent, the street fighter environment can be run at 600%+ the normal gameplay speed. And For hogwild training with 8 random agents, the environment can be run at 550%+ the normal gameplay speed. +With a single random agent, the street fighter environment can be run at 600%+ the normal gameplay speed. And For hogwild training with 8 random agents, the environment can be run at 300%+ the normal gameplay speed. ## Simple ConvNet Agent To ensure that the toolkit is able to train algorithms, a simple 5 layer ConvNet was setup with minimal tuning. The algorithm was able to successfully learn some simple mechanics of Street Fighter, such as combos and blocking. The Street Fighter gameplay works by having the player fight different opponents across 10 stages of increasing difficulty. Initially, the algorithm would reach stage 2 on average, but eventually could reach stage 5 on average after 2200 episodes of training. The learning rate was tracked using the net damage done vs damage taken of a single playthough for each episode. diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 0c6e3bb..0000000 --- a/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from emulator import * -import sf_environment diff --git a/setup.py b/setup.py index 2c9191c..8cc9306 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,22 @@ -import setuptools +from setuptools import setup, find_packages with open("README.md", "r") as fh: long_description = fh.read() -setuptools.setup( +setup( name="MAMEToolkit", - version="0.0.1", + version="1.0.0", author="Michael Murray", author_email="m.j.murray123@gmail.com", description="A library to train your RL algorithms against MAME arcade games on Linux", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/M-J-Murray/MAMEToolkit", - packages=setuptools.find_packages(), + packages=find_packages(exclude=['MAMEToolkit/emulator/mame/roms']), + include_package_data=True, classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", - "Operating System :: POSIX :: Linux", + "Operating System :: POSIX :: Linux" ], ) \ No newline at end of file diff --git a/src/emulator/mame/cfg/sfiii3n.cfg b/src/emulator/mame/cfg/sfiii3n.cfg deleted file mode 100644 index b01f5f7..0000000 --- a/src/emulator/mame/cfg/sfiii3n.cfg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/test/emulator/pipes/AddressTest.py b/test/emulator/AddressTest.py similarity index 94% rename from test/emulator/pipes/AddressTest.py rename to test/emulator/AddressTest.py index b6673fd..adab96c 100755 --- a/test/emulator/pipes/AddressTest.py +++ b/test/emulator/AddressTest.py @@ -1,6 +1,6 @@ import unittest from hamcrest import * -from src.emulator.pipes.Address import Address +from MAMEToolkit.emulator.Address import Address class AddressTest(unittest.TestCase): diff --git a/test/emulator/ConsoleTest.py b/test/emulator/ConsoleTest.py index 4399d8c..d6d3bde 100755 --- a/test/emulator/ConsoleTest.py +++ b/test/emulator/ConsoleTest.py @@ -2,13 +2,13 @@ from hamcrest import * from time import sleep from multiprocessing import set_start_method, Process, Queue -from src.emulator.Console import Console +from MAMEToolkit.emulator.Console import Console def run_console(game_id, output_queue): console = None try: - console = Console(game_id) + console = Console("MAMEToolkit/emulator/mame/roms", game_id) sleep(5) console.writeln('s = manager:machine().screens[":screen"]') output = console.writeln('print(s:width())', expect_output=True) @@ -23,7 +23,7 @@ def test_write_read(self): game_id = "sfiii3n" console = None try: - console = Console(game_id) + console = Console("/home/michael/dev/MAMEToolkit/MAMEToolkit/emulator/mame/roms", game_id) sleep(5) console.writeln('s = manager:machine().screens[":screen"]') output = console.writeln('print(s:width())', expect_output=True) diff --git a/test/emulator/EmulatorTest.py b/test/emulator/EmulatorTest.py index edfbbd4..596c727 100755 --- a/test/emulator/EmulatorTest.py +++ b/test/emulator/EmulatorTest.py @@ -1,15 +1,15 @@ import unittest from hamcrest import * -from src.emulator.Emulator import Emulator -from src.emulator.pipes.Address import Address +from MAMEToolkit.emulator.Emulator import Emulator +from MAMEToolkit.emulator.Address import Address from multiprocessing import set_start_method, Process, Queue -def run_emulator(env_id, game_id, memory_addresses, output_queue): +def run_emulator(env_id, game_id, roms_path, memory_addresses, output_queue): emulator = None try: - emulator = Emulator(env_id, game_id, memory_addresses) + emulator = Emulator(env_id, roms_path, game_id, memory_addresses) output_queue.put(emulator.step([])) finally: emulator.close() @@ -22,7 +22,7 @@ def test_screen_dimensions(self): game_id = "sfiii3n" emulator = None try: - emulator = Emulator("testEnv1", game_id, memory_addresses) + emulator = Emulator("testEnv1", "/home/michael/dev/MAMEToolkit/MAMEToolkit/emulator/mame/roms", game_id, memory_addresses) assert_that(emulator.screenDims["width"], equal_to(384)) assert_that(emulator.screenDims["height"], equal_to(224)) finally: @@ -46,7 +46,7 @@ def test_multiprocessing(self): game_id = "sfiii3n" memory_addresses = {"test": Address("02000008", "u8")} output_queue = Queue() - processes = [Process(target=run_emulator, args=[f"testEnv{i}", game_id, memory_addresses, output_queue]) for i in range(workers)] + processes = [Process(target=run_emulator, args=[f"testEnv{i}", "/home/michael/dev/MAMEToolkit/MAMEToolkit/emulator/mame/roms", game_id, memory_addresses, output_queue]) for i in range(workers)] [process.start() for process in processes] [process.join() for process in processes] for i in range(workers): diff --git a/test/emulator/StreamGobblerTest.py b/test/emulator/StreamGobblerTest.py index 489631c..47d3dc5 100755 --- a/test/emulator/StreamGobblerTest.py +++ b/test/emulator/StreamGobblerTest.py @@ -1,6 +1,6 @@ import unittest from hamcrest import * -from src.emulator.StreamGobbler import StreamGobbler +from MAMEToolkit.emulator.StreamGobbler import StreamGobbler from multiprocessing import set_start_method, Process, Queue import queue diff --git a/test/emulator/pipes/DataPipeTest.py b/test/emulator/pipes/DataPipeTest.py index 9370962..bf5da5a 100755 --- a/test/emulator/pipes/DataPipeTest.py +++ b/test/emulator/pipes/DataPipeTest.py @@ -3,8 +3,8 @@ from threading import Thread from queue import Queue as DefaultQueue -from src.emulator.pipes.DataPipe import DataPipe -from src.emulator.pipes.Address import Address +from MAMEToolkit.emulator.pipes.DataPipe import DataPipe +from MAMEToolkit.emulator.Address import Address from multiprocessing import set_start_method, Process, Queue as MPQueue diff --git a/test/emulator/pipes/PipeTest.py b/test/emulator/pipes/PipeTest.py index 0744fbb..ab236f4 100755 --- a/test/emulator/pipes/PipeTest.py +++ b/test/emulator/pipes/PipeTest.py @@ -4,7 +4,7 @@ from queue import Queue as DefaultQueue from multiprocessing import set_start_method, Process, Queue as MPQueue -from src.emulator.pipes.Pipe import Pipe +from MAMEToolkit.emulator.pipes.Pipe import Pipe import os os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))