google-deepmind / open_spiel

OpenSpiel is a collection of environments and algorithms for research in general reinforcement learning and search/planning in games.
Apache License 2.0
4.17k stars 920 forks source link

CMake struggles #392

Closed xgshark closed 3 years ago

xgshark commented 3 years ago

I am trying to open the examples with a vscode debugger under Linux to get a better understanding what is happening in there.

I could only figure out one way to change and build those which is to run open_spiel/scripts/build_and_run_tests.sh again after editing. When I change BUILD_TYPE in CMakeList.txt to Debug I get those symbols supposedly. I do not get vscode to find the correct source file for that executable or something either way the graphical debugger does not work.

I get the debugger to work with a simple 5 line test source file that I edited and compiled myself in the examples directory.

How can I build a source file that uses the open_spiel libraries? I tried this:

g++ cfr_example.cc -I../ -I../../ -I../abseil-cpp/ -std=c++17 -g

I get this: In file included from ../../open_spiel/spiel.h:36, from ../../open_spiel/policy.h:25, from ../../open_spiel/games/universal_poker.h:28, from main.cc:4: ../../open_spiel/game_parameters.h:132:13: error: explicit specialization in non-namespace scope ‘class open_spiel::GameParameter’ 132 | template <> | ^ ../../open_spiel/game_parameters.h:136:13: error: explicit specialization in non-namespace scope ‘class open_spiel::GameParameter’ 136 | template <> ...

Sorry if this is very basic. The code is incredibly well commented and very accessible. I spent two days trying to figure out this building stuff though.

lanctot commented 3 years ago

Hi @xgshark, it's not very basic. :) Yeah that will not work. The cfr_example depends on a lot of the internal code and needs to compile + link with it. This is what the CMake setup helps with: it takes care of all of the dependencies so you don't have to manually.

In our next release, we are intending to include binary-compiled libraries that will let you compile the way you're trying to do now. That's a few months away. But, we already have the target needed to build a binary library for your platform, which I suspect will help compile in the way you're trying to above.

To do that, run this:

BUILD_TYPE=Debug BUILD_SHARED_LIB=ON ./open_spiel/scripts/build_and_run_tests.sh --build_only=true

(Ignore that final error message, this is something I still need to fix on my side)

This will create a build/libopen_spiel.so which is pure gold, and what we intend to distribute in the next release. Now if you copy the C++ example code below into example_noflags.cc (absl::flags cannot be included in shared libraries, this is known from the absl team. I still have to look more into this...) and then run the following

export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PWD/build"
cd open_spiel/examples
# g++ might work too but that error you show above required us to move to clang >= 9
# my guess is you'll need clang 9 or clang 10
clang++ -o example_noflags example_noflags.cc -I../ -I../../ -I../abseil-cpp/ -std=c++17 -g -L../../build -lopen_spiel
./example_noflags

Should do it. Here's the contents of the file (it's basically example.cc with the absl::flags removed):


#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) {
  //absl::ParseCommandLine(argc, argv);

  std::string game_name = "tic_tac_toe";
  int players = 0;
  bool show_infostate = false;
  int seed = 0;
  bool show_legals = true;

  // 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(seed ? seed : time(0));

  // Create the game.
  std::cerr << "Creating game..\n" << std::endl;

  // Add any specified parameters to override the defaults.
  open_spiel::GameParameters params;
  if (players > 0) {
    params["players"] = open_spiel::GameParameter(players);
  }
  std::shared_ptr<const open_spiel::Game> game =
      open_spiel::LoadGame(game_name, params);

  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) {
        if (show_infostate) {
          if (game->GetType().provides_information_state_tensor) {
            state->InformationStateTensor(player, absl::MakeSpan(infostate));
            std::cerr << "player " << player << ": "
                      << absl::StrJoin(infostate, " ") << std::endl;
          }
          if (game->GetType().provides_information_state_string) {
            std::cerr << "player " << player << ": "
                      << state->InformationStateString(player) << std::endl;
          }
        }

        std::vector<open_spiel::Action> actions = state->LegalActions(player);
        if (show_legals) {
          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();
      if (show_infostate) {
        if (game->GetType().provides_information_state_tensor) {
          std::vector<float> infostate;
          state->InformationStateTensor(player, absl::MakeSpan(infostate));
          std::cerr << "player " << player << ": "
                    << absl::StrJoin(infostate, " ") << std::endl;
        }
        if (game->GetType().provides_information_state_string) {
          std::cerr << "player " << player << ": "
                    << state->InformationStateString(player) << std::endl;
        }
      }

      std::vector<open_spiel::Action> actions = state->LegalActions();
      if (show_legals) {
        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;
  }
}

Hope this helps. This will be a lot smoother in a few months.

inejc commented 3 years ago

@xgshark I'm not sure this helps with your original problem but the following vscode (build + debug) setup works for me:

The above launches the specified program and prints the output in the vscode debug console.

Hope this helps, let me know if you have additional questions.

xgshark commented 3 years ago

Works now, thank you Marc and Nejc for your detailed help.

In case somebody has the same issue and follows the instructions step by step: Only extra thing I had to do was to make the libopen_spiel.so discoverable for the system f.e.:

sudo cp build/libopen_spiel.so /usr/local/lib/ && sudo rm /etc/ld.so.cache && sudo ldconfig

lanctot commented 3 years ago

Great!