# 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 `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`.

1.  Build and run tests

    ```bash
    ./open_spiel/scripts/build_and_run_tests.sh
    ```

1.  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](https://github.com/deepmind/open_spiel/pull/187#issuecomment-616540881)).
    To fix it, you need to install the latest `libcxxwrap` by following the
    instructions
    [here](https://github.com/JuliaInterop/libcxxwrap-julia#building-libcxxwrap-julia)
    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:

```julia
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](https://github.com/JuliaInterop/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.

1.  `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.

1.  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.
