-
Notifications
You must be signed in to change notification settings - Fork 947
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #579 from deepmind:lanctot_open_spiel_library
PiperOrigin-RevId: 372350844 Change-Id: Id126e1abbe8e19cdfa4cfd1ae8997e0addd7733d
- Loading branch information
Showing
4 changed files
with
199 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# Using OpenSpiel as a C++ Library | ||
|
||
OpenSpiel has been designed as a framework: a suite of games, algorithms, and | ||
tools for research in reinforcement learning and search in games. However, there | ||
are situations where one may only want or need a single game/algorithm or small | ||
subset from this collection, or a research experiment does not require modifying | ||
or otherwise interacting very closely with OpenSpiel other than strictly | ||
calling/using it. | ||
|
||
In cases like this, it might be nice to use OpenSpiel as a library rather than a | ||
framework. This has the benefit of not forcing the use of certain tools like | ||
CMake or having to continually recompile OpenSpiel when doing your research. | ||
|
||
Luckily, this is easy to achieve with OpenSpiel: you simply need to build it as | ||
a shared library once, and then load it dynamically at runtime. This page walks | ||
through how to do this assuming a bash shell on Linux, but is very similar on | ||
MacOS or for other shells. | ||
|
||
## Compiling OpenSpiel as a Shared Library | ||
|
||
To build OpenSpiel as a shared library, simply run: | ||
|
||
``` | ||
mkdir build | ||
cd build | ||
BUILD_SHARED_LIB=ON CXX=clang++ cmake -DPython3_EXECUTABLE=$(which python3) -DCMAKE_CXX_COMPILER=${CXX} ../open_spiel | ||
make -j$(nproc) open_spiel | ||
``` | ||
|
||
This produces a dynamically-linked library `libopen_spiel.so` (or | ||
`lib_openspiel.dylib` on MacOS) in `build/` that can be linked against and | ||
loaded dynamically at run-time. | ||
|
||
Suppose OpenSpiel was installed in `$HOME/open_spiel`. The following line adds | ||
the necessary environment variable to let the shell know where to find | ||
`libopen_spiel.so` at run-time: | ||
|
||
``` | ||
export LD_LIBRARY_PATH="${HOME}/open_spiel/build" | ||
``` | ||
|
||
You might want to add this line to your `$HOME/.bash_profile` to avoid having to | ||
do it every time you load the library. Of course, if you are already using | ||
`LD_LIBRARY_PATH` something else, then you need to add | ||
`${HOME}/open_spiel/build` to it (space-separated paths). | ||
|
||
## Compiling and Running the Example | ||
|
||
``` | ||
cd ../open_spiel/examples | ||
clang++ -I${HOME}/open_spiel -I${HOME}/open_spiel/open_spiel/abseil-cpp \ | ||
-L${HOME}/open_spiel/build -lopen_spiel -std=c++17 \ | ||
-o shared_library_example shared_library_example.cc | ||
``` | ||
|
||
The first two flags are the include directory paths and the third is the link | ||
directory path. The `-lopen_spiel` instructs the linker to link against the | ||
OpenSpiel shared library. | ||
|
||
That's it! Now you can run the example using: | ||
|
||
``` | ||
./shared_library_example breakthrough | ||
``` | ||
|
||
You should also be able to register new games externally without the | ||
implementation being within OpenSpiel nor built into the shared library, though | ||
we are always interested in growing the library and recommend you contact us | ||
about contributing any new games to the suite. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// Copyright 2019 DeepMind Technologies Ltd. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// This is a simple example that is used to demonstrate how to use OpenSpiel | ||
// when it is built as a shared library. To use OpenSpiel as a library, | ||
// see: https://github.com/deepmind/open_spiel/blob/master/docs/library.md | ||
|
||
#include <unistd.h> | ||
|
||
#include <memory> | ||
#include <random> | ||
|
||
#include "open_spiel/abseil-cpp/absl/random/uniform_int_distribution.h" | ||
#include "open_spiel/spiel.h" | ||
#include "open_spiel/spiel_utils.h" | ||
|
||
void PrintLegalActions(const open_spiel::State& state, | ||
open_spiel::Player player, | ||
const std::vector<open_spiel::Action>& movelist) { | ||
std::cerr << "Legal moves for player " << player << ":" << std::endl; | ||
for (open_spiel::Action action : movelist) { | ||
std::cerr << " " << state.ActionToString(player, action) << std::endl; | ||
} | ||
} | ||
|
||
int main(int argc, char** argv) { | ||
if (argc < 2) { | ||
std::cerr << "Usage shared_library_example <game string>" << std::endl; | ||
return -1; | ||
} | ||
|
||
// Print out registered games. | ||
std::cerr << "Registered games:" << std::endl; | ||
std::vector<std::string> names = open_spiel::RegisteredGames(); | ||
for (const std::string& name : names) { | ||
std::cerr << name << std::endl; | ||
} | ||
|
||
// Random number generator. | ||
std::mt19937 rng(time(0)); | ||
|
||
// Load the game. | ||
std::cerr << "Loading game..\n" << std::endl; | ||
std::shared_ptr<const open_spiel::Game> game = open_spiel::LoadGame(argv[1]); | ||
|
||
if (!game) { | ||
std::cerr << "problem with loading game, exiting..." << std::endl; | ||
return -1; | ||
} | ||
|
||
std::cerr << "Starting new game..." << std::endl; | ||
std::unique_ptr<open_spiel::State> state = game->NewInitialState(); | ||
|
||
std::cerr << "Initial state:" << std::endl; | ||
std::cerr << "State:" << std::endl << state->ToString() << std::endl; | ||
|
||
while (!state->IsTerminal()) { | ||
std::cerr << "player " << state->CurrentPlayer() << std::endl; | ||
|
||
if (state->IsChanceNode()) { | ||
// Chance node; sample one according to underlying distribution. | ||
std::vector<std::pair<open_spiel::Action, double>> outcomes = | ||
state->ChanceOutcomes(); | ||
open_spiel::Action action = open_spiel::SampleAction(outcomes, rng).first; | ||
std::cerr << "sampled outcome: " | ||
<< state->ActionToString(open_spiel::kChancePlayerId, action) | ||
<< std::endl; | ||
state->ApplyAction(action); | ||
} else if (state->IsSimultaneousNode()) { | ||
// open_spiel::Players choose simultaneously? | ||
std::vector<open_spiel::Action> joint_action; | ||
std::vector<float> infostate(game->InformationStateTensorSize()); | ||
|
||
// Sample a action for each player | ||
for (auto player = open_spiel::Player{0}; player < game->NumPlayers(); | ||
++player) { | ||
std::vector<open_spiel::Action> actions = state->LegalActions(player); | ||
PrintLegalActions(*state, player, actions); | ||
|
||
absl::uniform_int_distribution<> dis(0, actions.size() - 1); | ||
open_spiel::Action action = actions[dis(rng)]; | ||
joint_action.push_back(action); | ||
std::cerr << "player " << player << " chose " | ||
<< state->ActionToString(player, action) << std::endl; | ||
} | ||
|
||
state->ApplyActions(joint_action); | ||
} else { | ||
// Decision node, sample one uniformly. | ||
auto player = state->CurrentPlayer(); | ||
std::vector<open_spiel::Action> actions = state->LegalActions(); | ||
PrintLegalActions(*state, player, actions); | ||
|
||
absl::uniform_int_distribution<> dis(0, actions.size() - 1); | ||
auto action = actions[dis(rng)]; | ||
std::cerr << "chose action: " << state->ActionToString(player, action) | ||
<< std::endl; | ||
state->ApplyAction(action); | ||
} | ||
|
||
std::cerr << "State: " << std::endl << state->ToString() << std::endl; | ||
} | ||
|
||
auto returns = state->Returns(); | ||
for (auto p = open_spiel::Player{0}; p < game->NumPlayers(); p++) { | ||
std::cerr << "Final return to player " << p << " is " << returns[p] | ||
<< std::endl; | ||
} | ||
} |