Julia OpenSpiel

We also provide a Julia wrapper for the OpenSpiel project. Most APIs are aligned with those in Python (some are extended to accept AbstractArray and/or keyword arguments for convenience). See spiel.h for the full API description.

Install

For general usage, you can install this package in the Julia REPL with ] add OpenSpiel. Note that this method only supports the Linux platform and ACPC is not included. For developers, you need to follow the instructions bellow to install this package:

  1. Install Julia and dependencies. Edit open_spiel/scripts/global_variables.sh and set OPEN_SPIELOPEN_SPIEL_BUILD_WITH_JULIA=ON (you may also turn on other options as you wish). Then run ./install.sh. If you already have Julia installed on your system, make sure that it is visible in your terminal and its version is v1.3 or later. Otherwise, Julia v1.3.1 will be automatically installed in your home dir and a soft link will be created at /usr/local/bin/julia.

  2. Build and run tests

    ./open_spiel/scripts/build_and_run_tests.sh
    
  3. Install ] dev ./open_spiel/julia (run in Julia REPL).

Known Problems

  1. There’s a problem when building this package on Mac with XCode v11.4 or above (see discussions here). To fix it, you need to install the latest libcxxwrap by following the instructions here after running ./install.sh. Then make sure that the result of julia --project=./open_spiel/julia -e 'using CxxWrap; print(CxxWrap.prefix_path())' points to the newly built libcxxwrap. After that, build and install this package as stated above.

Example

Here we demonstrate how to use the Julia API to play one game:

using OpenSpiel

# Here we need the StatsBase package for weighted sampling
using Pkg
Pkg.add("StatsBase")
using StatsBase

function run_once(name)
    game = load_game(name)
    state = new_initial_state(game)
    println("Initial state of game[$(name)] is:\n$(state)")

    while !is_terminal(state)
        if is_chance_node(state)
            outcomes_with_probs = chance_outcomes(state)
            println("Chance node, got $(length(outcomes_with_probs)) outcomes")
            actions, probs = zip(outcomes_with_probs...)
            action = actions[sample(weights(collect(probs)))]
            println("Sampled outcome: $(action_to_string(state, action))")
            apply_action(state, action)
        elseif is_simultaneous_node(state)
            chosen_actions = [rand(legal_actions(state, pid-1)) for pid in 1:num_players(game)]  # in Julia, indices start at 1
            println("Chosen actions: $([action_to_string(state, pid-1, action) for (pid, action) in enumerate(chosen_actions)])")
            apply_action(state, chosen_actions)
        else
            action = rand(legal_actions(state))
            println("Player $(current_player(state)) randomly sampled action: $(action_to_string(state, action))")
            apply_action(state, action)
        end
        println(state)
    end
    rts = returns(state)
    for pid in 1:num_players(game)
        println("Utility for player $(pid-1) is $(rts[pid])")
    end
end

run_once("tic_tac_toe")
run_once("kuhn_poker")
run_once("goofspiel(imp_info=True,num_cards=4,points_order=descending)")

Q&A

  1. What is StdVector?

    StdVector is introduced in CxxWrap.jl recently. It is a wrapper of std::vector in the C++ side. Since that it is a subtype of AbstractVector, most functions should just work out of the box.

  2. 0-based or 1-based?

    As this package is a low-level wrapper of OpenSpiel C++, most APIs are zero-based: for instance, the Player id starts from zero. But note that some bridge types, like StdVector, implicitly convert between indexing conventions, so APIs that use StdVector are one-based.

  3. I can’t find the xxx function/type in the Julia wrapper/The program exits unexpectedly.

    Although most of the functions and types should be exported, there is still a chance that some APIs are not well tested. So if you encounter any error, please do not hesitate to create an issue.