Home

Awesome

BeamOlympics

Beam Olympics

Let's find the fastest beamer!

Introduction

BeamOlympics is an Erlang app to help you check your Erlang-Fu. You can also play with friends in a local network.

Compilation

If you want to generate a release by hand, follow the steps below. If you would like to start with a pre-generated release, just download the latest one and jump to the Start section below.

Prerequisites

As stated in rebar.config, BeamOlympics server only compiles with Erlang/OTP19+.

Installation

Before you can start playing, you have to install the BeamOlympics server. To do so, clone this repo and use rebar3 to generate a release for it.

$ git clone https://github.com/inaka/beam_olympics.git
$ rebar3 release

If needed, between those 2 steps you can change your node name in vm.args to your convenience.

Start

To start the BeamOlympics server you you run…

$ $REL_PATH/beam_olympics/bin/beam_olympics start

Whre $REL_PATH would be _build/default/rel if you just generated the release with rebar3 or . if you downloaded a pre-compiled release from github.

That will boot up an Erlang node in your computer with the name specified by vm.args.

Players

Inside the node started in the previous step, a gen_server will be running. That server will act as the point of contact for clients to play the game. Every interaction will be accomplished through gen_server calls. For example, to check the overall game statistics, from a client node a caller can evaluate the following Elixir code:

GenServer.call({:bo_server, server_node}, :stats)

where server_node is a variable bound to the server node name (or a function that returns it).

General Rules

Commands

You can find the complete list of commands/calls with their possible answers below. Specs are written as functions although in reality the input is the call sent to the server and the output is the possible responses the server may return.


Signup

Using this call, the users register themselves in the game and receive their initial task.

Erlang Specs
{signup, PlayerName :: binary()} -> {ok, Task} | {error, conflict}.
Task :: #{ name := module()
         , desc := binary()
         , spec := Spec
         , score := pos_integer()
         }.
Spec :: #{ input := [binary()]
         , output := binary()
         }.
Elixir Specs
{:signup, player_name} :: {:ok, task} | {:error, :conflict}
  @type player_name :: String.t
  @type task :: %{
                  name: module,
                  desc: String.t,
                  spec: spec,
                  score: pos_integer
                 }
  @type spec :: %{
                  input: [String.t],
                  output: String.t
                 }
Details
Examples
gen_server:call({bo_server, ServerNode}, {signup, <<"Player">>}).
GenServer.call({:bo_server, server_node}, {:signup, "Player"})

Task

Retrieves the current task for the player.

Erlang Specs
{task, PlayerName :: binary()} ->
    {ok, Task} | {error, ended | forbidden | notfound}
Elixir Specs
{:task, player_name} :: {:ok, task} | {:error, :ended | :forbidden | :notfound}
Details
Examples
gen_server:call({bo_server, ServerNode}, {task, <<"Player">>}).
GenServer.call({:bo_server, server_node}, {:task, "Player"})

Submit

Submits a solution for the current task.

Erlang Specs
{submit, PlayerName :: binary(), Solution :: any()} ->
    {ok, Task}
  | the_end
  | {error, invalid | timeout | ended | forbidden | notfound}
  | {failures, [term(), ...]}
Elixir Specs
{:submit, player_name, term} ::
    {:ok, task} | :the_end |
    {:error, :invalid | :timeout | :ended | :forbidden | :notfound} |
    {:failures, [term,...]}
Details
Examples
gen_server:call(
    {bo_server, ServerNode}, {submit, <<"Player">>, fun() -> something end}).
GenServer.call(
    {:bo_server, server_node}, {:submit, "Player", fn() -> :something end})

Skip

Skips the current task.

Erlang Specs
{skip, PlayerName :: binary()} ->
    {ok, Task} | the_end | {error, ended | forbidden | notfound}
Elixir Specs
{:skip, player_name} ::
    {:ok, task} | :the_end | {:error, :ended | :forbidden | :notfound}
Details
Examples
gen_server:call({bo_server, ServerNode}, {skip, <<"Player">>}).
GenServer.call({:bo_server, server_node}, {:skip, "Player"})

Score

Retrieves the current score for the player.

Erlang Specs
{score, PlayerName :: binary()} -> {ok, integer()} | {error, forbidden | notfound}
Elixir Specs
{:score, player_name} :: {:ok, integer} | {:error, :forbidden | :notfound}
Details
Examples
gen_server:call({bo_server, ServerNode}, {score, <<"Player">>}).
GenServer.call({:bo_server, server_node}, {:score, "Player"})

Stats

Retrieves the current game stats.

Erlang Specs
stats -> #{ tasks := pos_integer()
          , players := [PlayerStats]
          }.
PlayerStats :: #{ name := binary()
                , done := non_neg_integer()
                , score := integer()
                }.
Elixir Specs
:stats :: stats
  @type stats :: %{
                    tasks: pos_integer,
                    players: [player_stats]
                  }
  @type player_stats :: %{
                           name: String.t,
                           done: non_neg_integer,
                           score: integer
                         }
Details
Examples
gen_server:call({bo_server, ServerNode}, stats).
GenServer.call({:bo_server, server_node}, :stats)

Contact Us

If you find any bugs or have a problem while using this library, please open an issue in this repo (or a pull request :)).

And you can check all of our open-source projects at inaka.github.io.