Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiplayer player API support in ALE #405

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 84 additions & 8 deletions src/ale_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,49 @@ void ALEInterface::reset_game() { environment->reset(); }
// Indicates if the game has ended.
bool ALEInterface::game_over() const { return environment->isTerminal(); }

// The remaining number of lives. Can only be called in one player modes
int ALEInterface::lives(){
if (romSettings == nullptr) {
throw std::runtime_error("ROM not set");
}
else {
if (numPlayersActive() == 1) {
return romSettings->lives();
}
else {
throw std::runtime_error("called `lives` in a multiplayer mode. Call allLives() instead.");
}
}
}

// The remaining number of lives.
int ALEInterface::lives() {
std::vector<int> ALEInterface::allLives() {
if (romSettings == nullptr) {
throw std::runtime_error("ROM not set");
} else {
return romSettings->lives();
int num_players = this->numPlayersActive();
if(num_players == 1) {
return {
romSettings->lives()
};
}
else if (num_players == 2) {
return {
romSettings->lives(),
romSettings->livesP2()
};
}
else if(num_players == 4) {
return {
romSettings->lives(),
romSettings->livesP2(),
romSettings->livesP3(),
romSettings->livesP4()
};
}
else{
throw std::runtime_error("ALE only support 1,2 and 4 players");
}
}
}

Expand All @@ -280,37 +317,76 @@ int ALEInterface::lives() {
// when necessary - this method will keep pressing buttons on the
// game over screen.
reward_t ALEInterface::act(Action action) {
reward_t reward = environment->act(action, PLAYER_B_NOOP);
reward_t reward = environment->act({action})[0];
if (theOSystem->p_display_screen != NULL) {
theOSystem->p_display_screen->display_screen();
while (theOSystem->p_display_screen->manual_control_engaged()) {
Action user_action = theOSystem->p_display_screen->getUserAction();
reward += environment->act(user_action, PLAYER_B_NOOP);
reward += environment->act({user_action})[0];
theOSystem->p_display_screen->display_screen();
}
}
return reward;
}

// Takes a vector of actions, one for each player in the game mode
// Does not allow user input from the screen
std::vector<reward_t> ALEInterface::act(std::vector<Action> action) {
if (romSettings == nullptr) {
throw std::runtime_error("ROM not set");
}
if (action.size() != numPlayersActive()) {
throw std::runtime_error("number of players active in the mode is not equal to the action size given to act");
}

return environment->act(action);
}

// Returns the vector of modes available for the current game.
// This should be called only after the rom is loaded.
ModeVect ALEInterface::getAvailableModes() {
return romSettings->getAvailableModes();
ModeVect ALEInterface::getAvailableModes(int num_players) {
if(num_players == 1){
return romSettings->getAvailableModes();
}
else if(num_players == 2){
return romSettings->get2PlayerModes();
}
else if(num_players == 3){
return ModeVect{};
}
else if(num_players == 4){
return romSettings->get4PlayerModes();
}
else {
throw std::runtime_error(std::to_string(num_players)+" is not a valid number of players, only 1-2 players allowed.");
}
}

// Sets the mode of the game.
// The mode must be an available mode.
// This should be called only after the rom is loaded.
void ALEInterface::setMode(game_mode_t m) {
//We first need to make sure m is an available mode
// We first need to make sure m is an available mode
ModeVect available = romSettings->getAvailableModes();
if (find(available.begin(), available.end(), m) != available.end()) {
ModeVect available2P = romSettings->get2PlayerModes();
ModeVect available4P = romSettings->get4PlayerModes();

available.insert(available.end(),available2P.begin(),available2P.end());
available.insert(available.end(),available4P.begin(),available4P.end());

if (std::find(available.begin(), available.end(), m) != available.end()) {
environment->setMode(m);
} else {
throw std::runtime_error("Invalid game mode requested");
}
}

// Number of players active in the current game mode
// also the, number of actions expected by act
int ALEInterface::numPlayersActive() {
return environment->getState().getNumActivePlayers();
}

//Returns the vector of difficulties available for the current game.
//This should be called only after the rom is loaded.
DifficultyVect ALEInterface::getAvailableDifficulties() {
Expand Down
15 changes: 13 additions & 2 deletions src/ale_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ class ALEInterface {
// when necessary - this method will keep pressing buttons on the
// game over screen.
reward_t act(Action action);
// multiplayer version of the act function.
// takes in one action per player and
// returns one reward per player
std::vector<reward_t> act(std::vector<Action> action);

// Indicates if the game has ended.
bool game_over() const;
Expand All @@ -101,7 +105,7 @@ class ALEInterface {

// Returns the vector of modes available for the current game.
// This should be called only after the rom is loaded.
ModeVect getAvailableModes();
ModeVect getAvailableModes(int num_players=1);

// Sets the mode of the game.
// The mode must be an available mode (otherwise it throws an exception).
Expand All @@ -113,6 +117,10 @@ class ALEInterface {
// game mode changes only take effect when the environment is reset.
game_mode_t getMode() const { return environment->getMode(); }

// Number of players active in the current game mode
// also the, number of actions expected by act
int numPlayersActive();

//Returns the vector of difficulties available for the current game.
//This should be called only after the rom is loaded. Notice
// that there are 2 levers, the right and left switches. They
Expand Down Expand Up @@ -145,9 +153,12 @@ class ALEInterface {
// Returns the frame number since the loading of the ROM
int getFrameNumber();

// The remaining number of lives.
// The remaining number of lives for player 1.
int lives();

// lives for all players
std::vector<int> allLives();

// Returns the frame number since the start of the current episode
int getEpisodeFrameNumber() const;

Expand Down
25 changes: 17 additions & 8 deletions src/ale_python_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@ class ALEPythonInterface : public ALEInterface {
py::array_t<pixel_t, py::array::c_style> getScreenRGB();
py::array_t<pixel_t, py::array::c_style> getScreenGrayscale();

inline reward_t act(unsigned int action) {
return ALEInterface::act((Action)action);
}

inline py::tuple getScreenDims() {
const ALEScreen& screen = ALEInterface::getScreen();
return py::make_tuple(screen.height(), screen.width());
Expand All @@ -58,6 +54,16 @@ class ALEPythonInterface : public ALEInterface {

} // namespace ale


inline std::vector<ale::Action> convert(std::vector<uint32_t> a){
std::vector<ale::Action> v(a.size());
for (size_t i = 0; i < a.size(); i++) {
v[i] = (ale::Action)(a[i]);
}
return v;
}


PYBIND11_MODULE(ale_py, m) {
m.attr("__version__") = py::str(ALE_VERSION_STR);
#ifdef __USE_SDL
Expand Down Expand Up @@ -128,13 +134,15 @@ PYBIND11_MODULE(ale_py, m) {
.def("setFloat", &ale::ALEPythonInterface::setFloat)
.def("loadROM", &ale::ALEPythonInterface::loadROM)
.def_static("isSupportedRom", &ale::ALEPythonInterface::isSupportedRom)
.def("act", (ale::reward_t(ale::ALEPythonInterface::*)(uint32_t)) &
ale::ALEPythonInterface::act)
.def("act", (ale::reward_t(ale::ALEInterface::*)(ale::Action)) &
ale::ALEInterface::act)
.def("act", [](ale::ALEPythonInterface & ale, uint32_t a){ return ale.act((ale::Action)(a)); })
.def("act", [](ale::ALEPythonInterface & ale, ale::Action a){ return ale.act(a); })
.def("act", [](ale::ALEPythonInterface & ale, std::vector<ale::Action> a){ return ale.act(a); })
.def("act", [](ale::ALEPythonInterface & ale, std::vector<uint32_t> a){ return ale.act(convert(a)); })
.def("game_over", &ale::ALEPythonInterface::game_over)
.def("reset_game", &ale::ALEPythonInterface::reset_game)
.def("numPlayersActive", &ale::ALEPythonInterface::numPlayersActive)
.def("getAvailableModes", &ale::ALEPythonInterface::getAvailableModes)
.def("getAvailableModes", [](ale::ALEPythonInterface & ale){ return ale.getAvailableModes(); })
.def("setMode", &ale::ALEPythonInterface::setMode)
.def("getAvailableDifficulties",
&ale::ALEPythonInterface::getAvailableDifficulties)
Expand All @@ -143,6 +151,7 @@ PYBIND11_MODULE(ale_py, m) {
.def("getMinimalActionSet", &ale::ALEPythonInterface::getMinimalActionSet)
.def("getFrameNumber", &ale::ALEPythonInterface::getFrameNumber)
.def("lives", &ale::ALEPythonInterface::lives)
.def("allLives", &ale::ALEPythonInterface::allLives)
.def("getEpisodeFrameNumber",
&ale::ALEPythonInterface::getEpisodeFrameNumber)
.def("getScreen", (void (ale::ALEPythonInterface::*)(
Expand Down
Loading