{"id":17964798,"url":"https://github.com/inaka/beam_olympics","last_synced_at":"2025-03-25T06:31:01.553Z","repository":{"id":57479521,"uuid":"61750982","full_name":"inaka/beam_olympics","owner":"inaka","description":"Let's find the fastest beamer!","archived":false,"fork":false,"pushed_at":"2018-03-12T19:02:14.000Z","size":81,"stargazers_count":39,"open_issues_count":6,"forks_count":6,"subscribers_count":36,"default_branch":"master","last_synced_at":"2025-03-19T09:21:11.115Z","etag":null,"topics":["erlang","erlang-learning","hacktoberfest","learning","learning-by-doing"],"latest_commit_sha":null,"homepage":"http://inaka.net/blog/2016/07/14/beam-olympics/","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inaka.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-06-22T20:56:25.000Z","updated_at":"2021-10-05T06:30:13.000Z","dependencies_parsed_at":"2022-09-17T05:02:34.914Z","dependency_job_id":null,"html_url":"https://github.com/inaka/beam_olympics","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fbeam_olympics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fbeam_olympics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fbeam_olympics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fbeam_olympics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inaka","download_url":"https://codeload.github.com/inaka/beam_olympics/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245413575,"owners_count":20611350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["erlang","erlang-learning","hacktoberfest","learning","learning-by-doing"],"created_at":"2024-10-29T12:08:45.039Z","updated_at":"2025-03-25T06:31:01.202Z","avatar_url":"https://github.com/inaka.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BeamOlympics\n\n![Beam Olympics](https://s3.amazonaws.com/uploads.hipchat.com/15025/55900/LeqcbfiQzCN48Lb/Beam%20Olympics%20black.png)\n\nLet's find the fastest beamer!\n\n## Introduction\n**BeamOlympics** is an Erlang app to help you check your Erlang-Fu. You can also play with friends in a local network.\n\n## Compilation\nIf 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](https://github.com/inaka/beam_olympics/releases) and jump to the [Start](#start) section below.\n\n### Prerequisites\nAs stated in rebar.config, **BeamOlympics** server only compiles with Erlang/OTP19+.\n\n### Installation\nBefore you can start playing, you have to install the **BeamOlympics** server.\nTo do so, clone this repo and use [rebar3](https://github.com/erlang/rebar3) to generate a release for it.\n\n```bash\n$ git clone https://github.com/inaka/beam_olympics.git\n$ rebar3 release\n```\n\nIf needed, between those 2 steps you can change your node name in [vm.args](https://github.com/inaka/beam_olympics/blob/master/config/vm.args#L2) to your convenience.\n\n## Start\nTo start the **BeamOlympics** server you you run…\n\n```bash\n$ $REL_PATH/beam_olympics/bin/beam_olympics start\n```\n\nWhre `$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.\n\nThat will boot up an Erlang node in your computer with the name specified by vm.args.\n\n## Players\nInside the node started in the previous step, a [gen_server](http://erldocs.com/maint/stdlib/gen_server.html) will be running. That server will act as the point of contact for clients to play the game.\nEvery 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:\n\n```elixir\nGenServer.call({:bo_server, server_node}, :stats)\n```\n\nwhere `server_node` is a variable bound to the server node name (or a function that returns it).\n\n### General Rules\n* To prevent fraud, users must always post solutions from the node in which they signed up. The node might crash/be closed, but when restarted it has to be called with the same name, otherwise submissions will be rejected.\n* There is no *undo*, if you skipped a task you can't choose to submit a solution for it later.\n* Each task has it's own associated score. Points are assigned this way:\n    - Each time a task is correctly solved, `score` points are added to the user\n    - Each time a task is skipped, `score / 2` points are deduced from the user\n* After skipping or completing a task, the user will either receive a new task or just the atom `the_end` indicating that the game is over\n\n### Commands\nYou 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.\n\n----\n#### Signup\nUsing this call, the users register themselves in the game and receive their initial task.\n\n##### Erlang Specs\n```erlang\n{signup, PlayerName :: binary()} -\u003e {ok, Task} | {error, conflict}.\nTask :: #{ name := module()\n         , desc := binary()\n         , spec := Spec\n         , score := pos_integer()\n         }.\nSpec :: #{ input := [binary()]\n         , output := binary()\n         }.\n```\n\n##### Elixir Specs\n```elixir\n{:signup, player_name} :: {:ok, task} | {:error, :conflict}\n  @type player_name :: String.t\n  @type task :: %{\n                  name: module,\n                  desc: String.t,\n                  spec: spec,\n                  score: pos_integer\n                 }\n  @type spec :: %{\n                  input: [String.t],\n                  output: String.t\n                 }\n```\n\n##### Details\n* The server will return `conflict` if the player is already registered.\n* The spec describes the function required to complete the task. `input` indicates the number and types of the parameters, `output` indicates the type of result it should produce.\n\n##### Examples\n```erlang\ngen_server:call({bo_server, ServerNode}, {signup, \u003c\u003c\"Player\"\u003e\u003e}).\n```\n\n```elixir\nGenServer.call({:bo_server, server_node}, {:signup, \"Player\"})\n```\n\n----\n#### Task\nRetrieves the current task for the player.\n\n##### Erlang Specs\n```erlang\n{task, PlayerName :: binary()} -\u003e\n    {ok, Task} | {error, ended | forbidden | notfound}\n```\n\n##### Elixir Specs\n```elixir\n{:task, player_name} :: {:ok, task} | {:error, :ended | :forbidden | :notfound}\n```\n\n##### Details\n* The server will return `ended` if the game is over for the player and therefore there is no current task\n* The server will return `forbidden` if the player is calling from a node that's not the one from which they signed up in the first place\n* The server will return `notfound` if the player doesn't exist\n\n##### Examples\n```erlang\ngen_server:call({bo_server, ServerNode}, {task, \u003c\u003c\"Player\"\u003e\u003e}).\n```\n\n```elixir\nGenServer.call({:bo_server, server_node}, {:task, \"Player\"})\n```\n\n----\n#### Submit\nSubmits a solution for the current task.\n\n##### Erlang Specs\n```erlang\n{submit, PlayerName :: binary(), Solution :: any()} -\u003e\n    {ok, Task}\n  | the_end\n  | {error, invalid | timeout | ended | forbidden | notfound}\n  | {failures, [term(), ...]}\n```\n\n##### Elixir Specs\n```elixir\n{:submit, player_name, term} ::\n    {:ok, task} | :the_end |\n    {:error, :invalid | :timeout | :ended | :forbidden | :notfound} |\n    {:failures, [term,...]}\n```\n\n##### Details\n* The server will return `ended` if the game is over for the player and therefore there is no current task\n* The server will return `forbidden` if the player is calling from a node that's not the one from which they signed up in the first place\n* The server will return `notfound` if the player doesn't exist\n* The server will return `timeout` if the evaluation of the tests took longer than expected\n* The server will return `invalid` if the arity of the provided function doesn't match the arity expected by the task or if the input is not even a function\n* The server will return `the_end` if the game is over for the player (i.e. there are no more tasks to complete/skip)\n* The server will return `failures` with a list of failure descriptions if there are tests for which the provided function fails\n\n##### Examples\n```erlang\ngen_server:call(\n    {bo_server, ServerNode}, {submit, \u003c\u003c\"Player\"\u003e\u003e, fun() -\u003e something end}).\n```\n\n```elixir\nGenServer.call(\n    {:bo_server, server_node}, {:submit, \"Player\", fn() -\u003e :something end})\n```\n\n----\n#### Skip\nSkips the current task.\n\n##### Erlang Specs\n```erlang\n{skip, PlayerName :: binary()} -\u003e\n    {ok, Task} | the_end | {error, ended | forbidden | notfound}\n```\n\n##### Elixir Specs\n```elixir\n{:skip, player_name} ::\n    {:ok, task} | :the_end | {:error, :ended | :forbidden | :notfound}\n```\n\n##### Details\n* The server will return `ended` if the game is over for the player and therefore there is no current task\n* The server will return `forbidden` if the player is calling from a node that's not the one from which they signed up in the first place\n* The server will return `notfound` if the player doesn't exist\n* The server will return `the_end` if the game is over for the player (i.e. there are no more tasks to complete/skip)\n\n##### Examples\n```erlang\ngen_server:call({bo_server, ServerNode}, {skip, \u003c\u003c\"Player\"\u003e\u003e}).\n```\n\n```elixir\nGenServer.call({:bo_server, server_node}, {:skip, \"Player\"})\n```\n\n----\n#### Score\nRetrieves the current score for the player.\n\n##### Erlang Specs\n```erlang\n{score, PlayerName :: binary()} -\u003e {ok, integer()} | {error, forbidden | notfound}\n```\n\n##### Elixir Specs\n```elixir\n{:score, player_name} :: {:ok, integer} | {:error, :forbidden | :notfound}\n```\n\n##### Details\n* The server will return `forbidden` if the player is calling from a node that's not the one from which they signed up in the first place\n* The server will return `notfound` if the player doesn't exist\n\n##### Examples\n```erlang\ngen_server:call({bo_server, ServerNode}, {score, \u003c\u003c\"Player\"\u003e\u003e}).\n```\n\n```elixir\nGenServer.call({:bo_server, server_node}, {:score, \"Player\"})\n```\n\n----\n#### Stats\nRetrieves the current game stats.\n\n##### Erlang Specs\n```erlang\nstats -\u003e #{ tasks := pos_integer()\n          , players := [PlayerStats]\n          }.\nPlayerStats :: #{ name := binary()\n                , done := non_neg_integer()\n                , score := integer()\n                }.\n```\n\n##### Elixir Specs\n```elixir\n:stats :: stats\n  @type stats :: %{\n                    tasks: pos_integer,\n                    players: [player_stats]\n                  }\n  @type player_stats :: %{\n                           name: String.t,\n                           done: non_neg_integer,\n                           score: integer\n                         }\n```\n\n##### Details\n* `tasks` will be the total number of tasks in the game\n* `done` will be the number of tasks already completed/skipped by the player\n\n##### Examples\n```erlang\ngen_server:call({bo_server, ServerNode}, stats).\n```\n\n```elixir\nGenServer.call({:bo_server, server_node}, :stats)\n```\n\n---\n\n## Contact Us\nIf you find any **bugs** or have a **problem** while using this library, please\n[open an issue](https://github.com/inaka/elvis/issues/new) in this repo\n(or a pull request :)).\n\nAnd you can check all of our open-source projects at [inaka.github.io](http://inaka.github.io).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finaka%2Fbeam_olympics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finaka%2Fbeam_olympics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finaka%2Fbeam_olympics/lists"}