{"id":13508216,"url":"https://github.com/DOBRO/binbo","last_synced_at":"2025-03-30T10:30:44.912Z","repository":{"id":38361737,"uuid":"196209466","full_name":"DOBRO/binbo","owner":"DOBRO","description":"Chess representation written in Erlang using Bitboards, ready for use on game servers","archived":false,"fork":false,"pushed_at":"2023-05-19T16:04:41.000Z","size":411,"stargazers_count":114,"open_issues_count":0,"forks_count":12,"subscribers_count":6,"default_branch":"master","last_synced_at":"2023-12-07T10:21:52.213Z","etag":null,"topics":["binbo","bitboards","chess","chess-game","chess-pgn","chess-uci","chessboard","chessboard-representation","erlang","erlang-chess","erlang-library","magic-bitboards","otp-library","pgn","uci","uci-protocol","universal-chess-interface"],"latest_commit_sha":null,"homepage":"","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/DOBRO.png","metadata":{"files":{"readme":"README.adoc","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-07-10T13:22:31.000Z","updated_at":"2023-12-20T15:53:47.524Z","dependencies_parsed_at":"2023-12-20T16:08:58.667Z","dependency_job_id":null,"html_url":"https://github.com/DOBRO/binbo","commit_stats":{"total_commits":405,"total_committers":2,"mean_commits":202.5,"dds":"0.0024691358024691024","last_synced_commit":"bc94da83ca373b9aa2d3971360ea41001ef24a9d"},"previous_names":[],"tags_count":20,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DOBRO%2Fbinbo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DOBRO%2Fbinbo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DOBRO%2Fbinbo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DOBRO%2Fbinbo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DOBRO","download_url":"https://codeload.github.com/DOBRO/binbo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246307578,"owners_count":20756473,"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":["binbo","bitboards","chess","chess-game","chess-pgn","chess-uci","chessboard","chessboard-representation","erlang","erlang-chess","erlang-library","magic-bitboards","otp-library","pgn","uci","uci-protocol","universal-chess-interface"],"created_at":"2024-08-01T02:00:49.894Z","updated_at":"2025-03-30T10:30:44.396Z","avatar_url":"https://github.com/DOBRO.png","language":"Erlang","funding_links":[],"categories":["Games","Erlang"],"sub_categories":[],"readme":"= Binbo\n:toc: macro\n:toclevels: 4\n\nimage:https://img.shields.io/hexpm/v/binbo.svg?color=yellow[\"Binbo on Hex.pm\", link=\"https://hex.pm/packages/binbo\"]\nimage:https://github.com/DOBRO/binbo/actions/workflows/main.yml/badge.svg?branch=master[\"CI Status\", link=\"https://github.com/DOBRO/binbo/actions?query=workflow%3ABuild+branch%3Amaster\"]\nimage:https://codecov.io/gh/DOBRO/binbo/branch/master/graph/badge.svg[\"Code coverage\", link=\"https://codecov.io/gh/DOBRO/binbo\"]\nimage:https://img.shields.io/badge/erlang-%3E%3D%2020.0-0d6e8c.svg[\"Erlang\", link=\"https://www.erlang.org/\"]\nimage:https://img.shields.io/badge/license-Apache%202.0-blue.svg[\"License\", link=\"LICENSE\"]\n\nBinbo is a full-featured Chess representation written in pure Erlang using https://www.chessprogramming.org/Bitboards[Bitboards]. It is basically aimed to be used on game servers where people play chess online.\n\nIt's called `Binbo` because its ground is a **bin**ary **bo**ard containing only _zeros_ and _ones_ (`0` and `1`) since this is the main meaning of Bitboards as an internal chessboard representation.\n\nBinbo also uses the https://www.chessprogramming.org/Magic_Bitboards[Magic Bitboards] approach for a **blazing fast** move generation of sliding pieces (rook, bishop, and queen).\n\n**Note:** it's not a chess engine but it could be a good starting point for it. It can play the role of a core (regarding move generation and validation) for multiple chess engines running on distributed Erlang nodes, since Binbo is an OTP application itself.\n\nIn addition, the application is able to communicate with https://www.chessprogramming.org/Category:UCI[chess engines that support UCI protocol] (https://www.chessprogramming.org/UCI[Universal Chess Interface]) such as _Stockfish_, _Shredder_, _Houdini_, etc. You can therefore write your own client-side or server-side **chess bot** application on top of Binbo, or just play with engine right in Erlang shell. TCP connections to remote chess engines are also supported.\n\nBinbo is part of the https://github.com/h4cc/awesome-elixir[Awesome Elixir] list.\n\nimage::https://user-images.githubusercontent.com/296845/61208986-40792d80-a701-11e9-93c8-d2c41c5ef00d.png[Binbo sample]\n\n'''\n\ntoc::[]\n\n'''\n\n== Features\n\n* Blazing fast move generation and validation.\n* No bottlenecks. Every game is an Erlang process (`gen_server`) with its own game state.\n* Ability to create as many concurrent games as many Erlang processes allowed in VM.\n* Support for PGN loading.\n* All the chess rules are completely covered including:\n** https://en.wikipedia.org/wiki/En_passant[En-passant move];\n** https://en.wikipedia.org/wiki/Castling[Castling];\n** https://en.wikipedia.org/wiki/Fifty-move_rule[Fifty-move rule];\n** https://en.wikipedia.org/wiki/Threefold_repetition[Threefold repetition];\n** Draw by insufficient material:\n*** King versus King,\n*** King and Bishop versus King,\n*** King and Knight versus King,\n*** King and Bishop versus King and Bishop with the bishops on the same color;\n* Unicode chess symbols support for the board visualization right in Erlang shell: +\n\u0026#9817;{nbsp}\u0026#9816;{nbsp}\u0026#9815;{nbsp}\u0026#9814;{nbsp}\u0026#9813;{nbsp}\u0026#9812;{nbsp}{nbsp}{nbsp}{nbsp}\u0026#9823;{nbsp}\u0026#9822;{nbsp}\u0026#9821;{nbsp}\u0026#9820;{nbsp}\u0026#9819;{nbsp}\u0026#9818;\n* UCI protocol support.\n* Support for TCP connections to remote UCI chess engines.\n* Passes all https://www.chessprogramming.org/Perft_Results[perft] tests.\n* Cross-platform application. It can run on Linux, Unix, Windows, and macOS.\n* Ready for use on game servers.\n\n== Requirements\n\n** https://www.erlang.org/[Erlang/OTP] 20.0 or higher.\n** https://www.rebar3.org/[rebar3]\n\n== Installation\n\n=== For Erlang projects\n\nAdd Binbo as a dependency to your `rebar.config` file:\n\n[source,erlang]\n----\n{deps, [\n  {binbo, \"4.0.3\"}\n]}.\n----\n\n=== For Elixir projects\n\nAdd Binbo as a dependency to your `mix.exs` file:\n\n[source,elixir]\n----\ndefp deps do\n  [\n    {:binbo, \"~\u003e 4.0\"}\n  ]\nend\n----\n\n== Quick start\n\nClone repository, change directory to `binbo` and run `rebar3 shell` (or `make shell`):\n\n[source,bash]\n----\n$ git clone https://github.com/DOBRO/binbo.git\n$ cd binbo\n$ rebar3 shell\n----\n\n=== Common example\n\n.In the Erlang shell:\n[source,erlang]\n----\n%% Start Binbo application first:\nbinbo:start().\n\n%% Start new process for the game:\n{ok, Pid} = binbo:new_server().\n\n%% Start new game in the process:\nbinbo:new_game(Pid).\n\n%% Or start new game with a given FEN:\nbinbo:new_game(Pid, \u003c\u003c\"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1\"\u003e\u003e).\n\n%% Look at the board with ascii or unicode pieces:\nbinbo:print_board(Pid).\nbinbo:print_board(Pid, [unicode]).\n\n%% Make move for White and Black:\nbinbo:move(Pid, \u003c\u003c\"e2e4\"\u003e\u003e).\nbinbo:move(Pid, \u003c\u003c\"e7e5\"\u003e\u003e).\n\n%% Have a look at the board again:\nbinbo:print_board(Pid).\nbinbo:print_board(Pid, [unicode]).\n----\n\n[[quickstart-play-with-engine]]\n=== Play with engine on local machine\n\n.In the Erlang shell:\n[source,erlang]\n----\n%% Start Binbo application first:\n\u003e binbo:start().\n{ok,[compiler,syntax_tools,uef,binbo]}\n\n%% Start new process for the game:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.157.0\u003e}\n\n%% Set full path to the engine's executable file:\n\u003e EnginePath = \"/usr/local/bin/stockfish\".\n\"/usr/local/bin/stockfish\"\n\n%% Start new game in the process:\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e EnginePath}).\n{ok,continue}\n\n%% Which side is to move?\n\u003e binbo:side_to_move(Pid).\n{ok,white}\n\n%% Say, you want to play Black. Tell the engine to make move for White.\n\u003e binbo:uci_play(Pid, #{}).\n{ok,continue,\u003c\u003c\"e2e4\"\u003e\u003e}\n\n%% Make your move for Black and get the engine's move immediately:\n\u003e binbo:uci_play(Pid, #{}, \u003c\u003c\"e7e5\"\u003e\u003e).\n{ok,continue,\u003c\u003c\"g1f3\"\u003e\u003e}   % the engine's move was \"g1f3\"\n\n%% Make your next move for Black and, again, get the engine's move at once:\n\u003e binbo:uci_play(Pid, #{}, \u003c\u003c\"b8c6\"\u003e\u003e).\n{ok,continue,\u003c\u003c\"b1c3\"\u003e\u003e}   % the engine's move was \"b1c3\"\n\n%% Look at the board with ascii or unicode pieces.\n%% Flip the board to see Black on downside:\nbinbo:print_board(Pid, [flip]).\nbinbo:print_board(Pid, [unicode, flip]).\n\n%% It's your turn now. Let the engine search for the best move for you with default options.\n%% No move actually done, just hint:\n\u003e binbo:uci_bestmove(Pid, #{}).\n{ok,\u003c\u003c\"g8f6\"\u003e\u003e}\n\n%% Tell the engine to search for the best move at depth 20:\n\u003e binbo:uci_bestmove(Pid, #{depth =\u003e 20}).\n{ok,\u003c\u003c\"g8f6\"\u003e\u003e}\n\n%% To make the gameplay more convenient, introduce new function:\n\u003e Play = fun(Move) -\u003e Result = binbo:uci_play(Pid, #{}, Move), binbo:print_board(Pid, [unicode, flip]), Result end.\n\n%% Now, with this function, go through three steps at once:\n%%   - make move \"g8f6\",\n%%   - get the engine's move,\n%%   - see how the position was changed.\n\u003e Play(\"g8f6\").\n----\n\n\u0026#8230; engine's move was \"d2d4\":\n[source]\n----\n\n   +---+---+---+---+---+---+---+---+\n 1 | ♖ |   | ♗ | ♔ | ♕ | ♗ |   | ♖ |\n   +---+---+---+---+---+---+---+---+\n 2 | ♙ | ♙ | ♙ |   |   | ♙ | ♙ | ♙ |\n   +---+---+---+---+---+---+---+---+\n 3 |   |   | ♘ |   |   | ♘ |   |   |\n   +---+---+---+---+---+---+---+---+\n 4 |   |   |   | ♙ | ♙ |   |   |   |\n   +---+---+---+---+---+---+---+---+\n 5 |   |   |   | ♟ |   |   |   |   |\n   +---+---+---+---+---+---+---+---+\n 6 |   |   | ♞ |   |   | ♞ |   |   |\n   +---+---+---+---+---+---+---+---+\n 7 | ♟ | ♟ | ♟ |   | ♟ | ♟ | ♟ | ♟ |\n   +---+---+---+---+---+---+---+---+\n 8 | ♜ |   | ♝ | ♚ | ♛ | ♝ |   | ♜ |\n   +---+---+---+---+---+---+---+---+\n     H   G   F   E   D   C   B   A\n\n  Side to move: Black\n  Lastmove: d2-d4, WHITE_PAWN\n  Fullmove: 4\n  Halfmove: 0\n  FEN: \"r1bqkb1r/pppp1ppp/2n2n2/4p3/3PP3/2N2N2/PPP2PPP/R1BQKB1R b KQkq d3 0 4\"\n  Status: continue\n\n{ok,continue,\u003c\u003c\"d2d4\"\u003e\u003e}\n----\n\n[[quickstart-uci-over-tcp]]\n=== Connect to remote engine over TCP\n\nThe examples below assume that Stockfish is used as the chess engine and its path is `/usr/local/bin/stockfish`, change it according to your environment.\nTCP service starts on local machine on port `9010`.\n\nIf you are on Linux, install `socat` and start TCP service. On macOS just use file `org.stockfish.x86.plist` (for Intel-based devices) or `org.stockfish.arm.plist` (for Apple silicon) provided in the `test` folder (see below).\n\n.On Ubuntu/Debian:\n[source,bash]\n----\n$ apt install socat -y\n$ socat TCP-LISTEN:9010,reuseaddr,fork EXEC:/usr/local/bin/stockfish \u0026\n$ git clone https://github.com/DOBRO/binbo.git\n$ cd binbo\n$ rebar3 shell\n----\n\n.On Centos/Fedora:\n[source,bash]\n----\n$ dnf install socat -y\n$ socat TCP-LISTEN:9010,reuseaddr,fork EXEC:/usr/local/bin/stockfish \u0026\n$ git clone https://github.com/DOBRO/binbo.git\n$ cd binbo\n$ rebar3 shell\n----\n\n.On macOS (Intel x86 Architecture):\n[source,bash]\n----\n$ git clone https://github.com/DOBRO/binbo.git\n$ cd binbo\n$ launchctl load test/helper-files/org.stockfish.x86.plist\n$ rebar3 shell\n----\n\n.On macOS (Apple silicon):\n[source,bash]\n----\n$ git clone https://github.com/DOBRO/binbo.git\n$ cd binbo\n$ launchctl load test/helper-files/org.stockfish.arm.plist\n$ rebar3 shell\n----\n\n.Now in the Erlang shell:\n[source,erlang]\n----\n%% Start Binbo application first:\n\u003e binbo:start().\n{ok,[compiler,syntax_tools,uef,binbo]}\n\n%% Start new process for the game:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.282.0\u003e}\n\n%% Set path to the remote engine as tuple {Host, Port, Timeout}:\n\u003e EnginePath = {\"localhost\", 9010, 5000}.\n{\"localhost\",9010,5000}\n\n%% Start new game in the process:\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e EnginePath}).\n{ok,continue}\n\n%% UCI-over-TCP connection made, start playing:\n\u003e binbo:uci_play(Pid, #{movetime =\u003e 100}, \u003c\u003c\"e2e4\"\u003e\u003e).\n{ok,continue,\u003c\u003c\"c7c5\"\u003e\u003e} % the engine's move was \"c7c5\"\n----\n\n== Interface\n\nThere are three steps to be done before making game moves:\n\n. Start Binbo application.\n. Create process for the game.\n. Initialize game state in the process.\n\n**Note:** process creation and game initialization are separated for the following reason: since Binbo is aimed to handle a number of concurrent games, the game process should be started as quick as possible leaving the http://erlang.org/doc/design_principles/sup_princ.html[supervisor] doing the same job for another game. It's important for high-load systems where game creation is a very frequent event.\n\n=== Starting application\n\nTo start Binbo, call:\n\n[source,erlang]\n----\nbinbo:start().\n----\n\n=== Creating game process\n\n[source,erlang]\n----\nbinbo:new_server() -\u003e {ok, Pid} | {error, Reason}.\nbinbo:new_server(Options) -\u003e {ok, Pid} | {error, Reason}.\n----\n\n.where:\n* `Pid` - pid of the created process;\n* `Options` - options for the game process (see link:#server-options[bellow]).\n\n.So, to start one or more game processes:\n[source,erlang]\n----\n{ok, Pid1} = binbo:new_server(),\n{ok, Pid2} = binbo:new_server(),\n{ok, Pid3} = binbo:new_server().\n----\n\n[[server-options]]\n==== Options for the game process\n[source,erlang]\n----\nbinbo:set_server_options(Pid, Options) -\u003e ok | {error, Reason}.\n----\n\n`Pid` is the `pid` of the game process.\n\n.`Options`:\n[source,erlang]\n----\n#{\n  idle_timeout =\u003e timeout(),\n  onterminate  =\u003e {fun my_callback/4, Arg}\n}\n----\n\n.where:\n* `idle_timeout` - time in milliseconds with no messages received before the game process exits. Defaults to `infinity`.\n* `onterminate` - tuple where the first element is a callback function that performs when process exits. This function must be of *arity 4* with argumnents `Pid`, `Reason`, `GameState`, and `Arg` where:\n** `Pid` - pid of the game process;\n** `Reason` - the reason why the game process exited;\n** `GameState` - the whole link:#game-state[game state];\n** `Arg` - the argument you want to pass to the callback function.\n\n.Example:\n[source,erlang]\n----\n-module(on_terminate).\n\n-export([run/0]).\n\nrun() -\u003e\n    binbo:start(),\n    {ok, Pid} = binbo:new_server(),\n    binbo:new_game(Pid),\n    binbo:set_server_options(Pid, #{\n        idle_timeout =\u003e 1000,\n        onterminate =\u003e {fun onterminate_callback/4, \"my argument\"}\n    }),\n    % 'onterminate_callback/4' will be called after 1000 ms\n    ok.\n\nonterminate_callback(GamePid, Reason, Game, Arg) -\u003e\n    io:format(\"GamePid: ~p~n\", [GamePid]),\n    io:format(\"Reason: ~p~n\", [Reason]),\n    io:format(\"Game: ~p~n\", [Game]),\n    io:format(\"Arg: ~p~n\", [Arg]),\n    ok.\n----\n\n.To reset options, call:\n[source,erlang]\n----\nbinbo:set_server_options(Pid, #{\n    idle_timeout =\u003e infinity,\n    onterminate =\u003e undefined\n})\n----\n\n\n[[initializing-new-game]]\n=== Initializing new game\n\n[source,erlang]\n----\nbinbo:new_game(Pid) -\u003e {ok, GameStatus} | {error, Reason}.\n\nbinbo:new_game(Pid, Fen) -\u003e {ok, GameStatus} | {error, Reason}.\n----\n\n.where:\n* `Pid` is the `pid` of the process where the game is to be initialized;\n* `Fen` (`string()` or `binary()`) is the https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation[Forsyth–Edwards Notation] (FEN);\n* `GameStatus` is the link:#game-status[game status].\n\nIt is possible to reinitialize game in the same process. For example:\n\n[source,erlang]\n----\nbinbo:new_game(Pid),\nbinbo:new_game(Pid, Fen2),\nbinbo:new_game(Pid, Fen3).\n----\n\n\n.Example:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.185.0\u003e}\n\n% New game from the starting position:\n\u003e binbo:new_game(Pid).\n{ok,continue}\n\n% New game with the given FEN:\n\u003e binbo:new_game(Pid, \u003c\u003c\"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1\"\u003e\u003e).\n{ok,continue}\n----\n\n=== Making moves\n\n==== API\n\n[source,erlang]\n----\nbinbo:move(Pid, Move) -\u003e {ok, GameStatus} | {error, Reason}.\n\nbinbo:san_move(Pid, Move) -\u003e {ok, GameStatus} | {error, Reason}.\n\nbinbo:index_move(Pid, FromIndex, ToIndex) -\u003e {ok, GameStatus} | {error, Reason}.\n\nbinbo:index_move(Pid, FromIndex, ToIndex, PromotionType) -\u003e {ok, GameStatus} | {error, Reason}.\n----\n\nwhere:\n\n* `Pid` is the pid of the game process;\n* `Move` is of `binary()` or `string()` type;\n* `GameStatus` is the link:#game-status[game status].\n* `FromIndex` - index of square a piece moves from.\n* `ToIndex` - index of square a piece moves to.\n* `PromotionType` - piece that a pawn should be promoted to, one of the atoms: `q`, `r`, `b`, `n` (_queen_, _rook_, _bishop_, _knight_). Defaults to `q` (_queen_).\n\nFunction `binbo:move/2` supports only _strict square notation_ with respect to argument `Move`, for example: `\u003c\u003c\"e2e4\"\u003e\u003e`, `\u003c\u003c\"e7e5\"\u003e\u003e`, etc.\n\nFunction `binbo:san_move/2` is intended to handle various formats of argument `Move` including https://en.wikipedia.org/wiki/Algebraic_notation_(chess)[_standard algebraic notation_] (*SAN*), for example: `\u003c\u003c\"e4\"\u003e\u003e`, `\u003c\u003c\"Nf3\"\u003e\u003e`, `\u003c\u003c\"Qxd5\"\u003e\u003e`, `\u003c\u003c\"a8=Q\"\u003e\u003e`, `\u003c\u003c\"Rdf8\"\u003e\u003e`, `\u003c\u003c\"R1a3\"\u003e\u003e`, `\u003c\u003c\"O-O\"\u003e\u003e`, `\u003c\u003c\"O-O-O\"\u003e\u003e`, `\u003c\u003c\"e1e8\"\u003e\u003e`, etc.\n\nFunction `binbo:index_move/3,4` takes only square indices for the second and third parameter. For example, `binbo:index_move(Pid, 12, 28)` is the same as `binbo:move(Pid, \u003c\u003c\"e2e4\"\u003e\u003e)`.\n\n.Examples for `binbo:move/2`:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% New game from the starting position:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.190.0\u003e}\n\u003e binbo:new_game(Pid).\n{ok,continue}\n\n% Start making moves\n\u003e binbo:move(Pid, \u003c\u003c\"e2e4\"\u003e\u003e). % e4\n{ok,continue}\n\n\u003e binbo:move(Pid, \u003c\u003c\"e7e5\"\u003e\u003e). % e5\n{ok,continue}\n\n\u003e binbo:move(Pid, \u003c\u003c\"f1c4\"\u003e\u003e). % Bc4\n{ok,continue}\n\n\u003e binbo:move(Pid, \u003c\u003c\"d7d6\"\u003e\u003e). % d6\n{ok,continue}\n\n\u003e binbo:move(Pid, \u003c\u003c\"d1f3\"\u003e\u003e). % Qf3\n{ok,continue}\n\n\u003e binbo:move(Pid, \u003c\u003c\"b8c6\"\u003e\u003e). % Nc6\n{ok,continue}\n\n% And here is checkmate!\n\u003e binbo:move(Pid, \u003c\u003c\"f3f7\"\u003e\u003e). % Qf7#\n{ok,{checkmate,white_wins}}\n----\n\n.Examples for `binbo:san_move/2`:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% New game from the starting position:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.190.0\u003e}\n\u003e binbo:new_game(Pid).\n{ok,continue}\n\n% Start making moves\n\u003e binbo:san_move(Pid, \u003c\u003c\"e4\"\u003e\u003e).\n{ok,continue}\n\n\u003e binbo:san_move(Pid, \u003c\u003c\"e5\"\u003e\u003e).\n{ok,continue}\n\n\u003e binbo:san_move(Pid, \u003c\u003c\"Bc4\"\u003e\u003e).\n{ok,continue}\n\n\u003e binbo:san_move(Pid, \u003c\u003c\"d6\"\u003e\u003e).\n{ok,continue}\n\n\u003e binbo:san_move(Pid, \u003c\u003c\"Qf3\"\u003e\u003e).\n{ok,continue}\n\n\u003e binbo:san_move(Pid, \u003c\u003c\"Nc6\"\u003e\u003e).\n{ok,continue}\n\n% Checkmate!\n\u003e binbo:san_move(Pid, \u003c\u003c\"Qf7#\"\u003e\u003e).\n{ok,{checkmate,white_wins}}\n----\n\n.Examples for `binbo:index_move/3`:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% New game from the starting position:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.190.0\u003e}\n\u003e binbo:new_game(Pid).\n{ok,continue}\n\n% Start making moves\n\u003e binbo:index_move(Pid, 12, 28). % e2-e4\n{ok,continue}\n\n\u003e binbo:index_move(Pid, 52, 36). % e7-e5\n{ok,continue}\n----\n\n==== Castling\n\nBinbo recognizes https://en.wikipedia.org/wiki/Castling[castling] when:\n\n* White king moves from `E1` to `G1` (`O-O`);\n* White king moves from `E1` to `C1` (`O-O-O`);\n* Black king moves from `E8` to `G8` (`O-O`);\n* Black king moves from `E8` to `C8` (`O-O-O`).\n\nBinbo also checks whether castling allowed or not acording to the chess rules.\n\n.Castling examples:\n[source,erlang]\n----\n% White castling kingside\nbinbo:move(Pid, \u003c\u003c\"e1g1\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"O-O\"\u003e\u003e).\n\n% White castling queenside\nbinbo:move(Pid, \u003c\u003c\"e1c1\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"O-O-O\"\u003e\u003e).\n\n% Black castling kingside\nbinbo:move(Pid, \u003c\u003c\"e8g8\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"O-O\"\u003e\u003e).\n\n% Black castling queenside\nbinbo:move(Pid, \u003c\u003c\"e8c8\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"O-O-O\"\u003e\u003e).\n----\n\n==== Promotion\n\nBinbo recognizes https://en.wikipedia.org/wiki/Promotion_(chess)[promotion] when:\n\n* White pawn moves from square of `rank 7` to square of `rank 8`;\n* Black pawn moves from square of `rank 2` to square of `rank 1`.\n\n.Promotion examples:\n[source,erlang]\n----\n% White pawn promoted to Queen:\nbinbo:move(Pid, \u003c\u003c\"a7a8q\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"a8=Q\"\u003e\u003e).\n% or just:\nbinbo:move(Pid, \u003c\u003c\"a7a8\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"a8\"\u003e\u003e).\n\n% White pawn promoted to Knight:\nbinbo:move(Pid, \u003c\u003c\"a7a8n\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"a8=N\"\u003e\u003e).\n\n% Black pawn promoted to Queen:\nbinbo:move(Pid, \u003c\u003c\"a2a1q\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"a1=Q\"\u003e\u003e).\n% or just:\nbinbo:move(Pid, \u003c\u003c\"a2a1\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"a1\"\u003e\u003e).\n\n% Black pawn promoted to Knight:\nbinbo:move(Pid, \u003c\u003c\"a2a1n\"\u003e\u003e).\nbinbo:san_move(Pid, \u003c\u003c\"a1=N\"\u003e\u003e).\n----\n\n==== En passant\n\nBinbo also recognizes the https://en.wikipedia.org/wiki/En_passant[en passant capture] in strict accordance with the chess rules.\n\n=== Getting FEN\n\n[source,erlang]\n----\nbinbo:get_fen(Pid) -\u003e {ok, Fen}.\n----\n\n.Example:\n[source,erlang]\n----\n\u003e binbo:get_fen(Pid).\n{ok, \u003c\u003c\"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1\"\u003e\u003e}.\n----\n\n=== PGN loading\n\n[source,erlang]\n----\nbinbo:load_pgn(Pid, PGN) -\u003e {ok, GameStatus} | {error, Reason}.\n\nbinbo:load_pgn_file(Pid, Filename) -\u003e {ok, GameStatus} | {error, Reason}.\n----\n\n.where:\n* `Pid` is the pid of the game process;\n* `PGN` is a https://en.wikipedia.org/wiki/Portable_Game_Notation[Portable Game Notation], its type is `binary()`;\n* `Filename` is a path to the file from which PGN is to be loaded. Its type is `binary()` or `string()`.\n* `GameStatus` is the link:#game-status[game status].\n\nFunction `binbo:load_pgn/2` loads PGN itself.\n\nIf `PGN` is pretty large and you are able to load it from *local* file, to avoid sending large data between processes, use `binbo:load_pgn_file/2` since it's highly optimized for reading local files.\n\nTo extract move list, Binbo takes into account various cases specific to PGN such as _comments in braces_,\nhttps://chess.stackexchange.com/questions/18214/valid-pgn-variations[_recursive annotation variations_] (RAVs) and\nhttps://en.wikipedia.org/wiki/Numeric_Annotation_Glyphs[_numeric annotation glyphs_] (NAGs).\n\n.Examples:\n[source,erlang]\n----\n%% Binary PGN:\nload_pgn() -\u003e\n  PGN = \u003c\u003c\"1. e4 e5 2. Nf3 Nc6 3. Bb5 a6\"\u003e\u003e,\n  {ok, Pid} = binbo:new_server(),\n  binbo:load_pgn(Pid, PGN).\n\n%% From file:\nload_pgn_from_file() -\u003e\n  Filename = \"/path/to/game.pgn\",\n  {ok, Pid} = binbo:new_server(),\n  binbo:load_pgn_file(Pid, Filename).\n----\n\n=== Board visualization\n\n[source,erlang]\n----\nbinbo:print_board(Pid) -\u003e ok.\nbinbo:print_board(Pid, [unicode|ascii|flip]) -\u003e ok.\n----\n\nYou may want to see the current position right in Elang shell. To do it, call:\n[source,erlang]\n----\n% With ascii pieces:\nbinbo:print_board(Pid).\n\n% With unicode pieces:\nbinbo:print_board(Pid, [unicode]).\n\n% Flipped board:\nbinbo:print_board(Pid, [flip]).\nbinbo:print_board(Pid, [unicode, flip]).\n----\n\n[[game-status]]\n=== Game status\n\n[source,erlang]\n----\nbinbo:game_status(Pid) -\u003e {ok, GameStatus} | {error, Reason}.\n----\n\n.where:\n* `Pid` is the the pid of the game process;\n* `GameStatus` is the game status itself;\n* `Reason` is the reason why the game status cannot be obtained (usually due to the fact that the game is not initialized via link:#initializing-new-game[binbo:new_game/1,2]).\n\n.The value of `GameStatus`:\n* `continue` - game in progress;\n* `{checkmate, white_wins}` - White wins, Black checkmated;\n* `{checkmate, black_wins}` - Black wins, White checkmated;\n* `{draw, stalemate}` - draw because of stalemate;\n* `{draw, rule50}` - draw according to the fifty-move rule;\n* `{draw, insufficient_material}` - draw because of insufficient material;\n* `{draw, threefold_repetition}` - draw according to the threefold repetition rule;\n* `{draw, {manual, WhyDraw}}` - draw was set link:#setting-a-draw[manually] for the reason of `WhyDraw`.\n* `{winner, Winner, {manual, WinnerReason}}` - winner `Winner` was set link:#setting-game-winner[manually] for the reason of `WinnerReason`.\n\n\n=== List of legal moves\n\n[source,erlang]\n----\nbinbo:all_legal_moves(Pid) -\u003e {ok, Movelist} | {error, Reason}.\n\nbinbo:all_legal_moves(Pid, Movetype) -\u003e {ok, Movelist} | {ok, Number} | {error, Reason}.\n----\n\n.where:\n* `Pid` is the pid of the game process;\n* `Movelist` is a list of all legal moves for the current position. Each element of `Movelist` is a tuple `{From, To}` or `{From, To, Promo}`, where:\n** `From` and `To` are starting and target square respectively.\n** `Promo` is one of the _atoms_: `q`, `r`, `b`, `n` (i.e. _queen_, _rook_, _bishop_, and _knight_ respectively). Three-element tuple `{From, To, Promo}` occurs in case of *pawn promotion*.\n* `Movetype` can take on of the values: `int`, `bin`, `str`, or `count`.\n\nThe call `binbo:all_legal_moves(Pid)` is the same as `binbo:all_legal_moves(Pid, int)`.\n\nIf `Movetype` is `count`, the function returns tuple `{ok, Number}` where `Number` is the number of legal moves.\n\nThe values of `From` and `To` depend on `Movetype` as follows:\n\n* `int`: the values of `From` and `To` are _integers_ in range `0..63`, namely, square indices. For example, the move from `A1` to `H8` corresponds to `{0, 63}`. Use `int` to get the *fastest* reply from the game process.\n* `bin`: the values of `From` and `To` are _binaries_. For example: `{\u003c\u003c\"e2\"\u003e\u003e, \u003c\u003c\"e4\"\u003e\u003e}`.\n* `str`: the values of `From` and `To` are _strings_. For example: `{\"e2\", \"e4\"}`.\n\n.Example:\n[source,erlang]\n----\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.212.0\u003e}\n\n%% Start new game from FEN that corresponds to Position 5\n%% from Perft Results: https://www.chessprogramming.org/Perft_Results\n\u003e binbo:new_game(Pid, \u003c\u003c\"rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8\"\u003e\u003e).\n{ok,continue}\n\n%% Count legal moves\n\u003e binbo:all_legal_moves(Pid, count).\n{ok,44}\n\n\u003e {ok, Movelist} = binbo:all_legal_moves(Pid).\n{ok,[{51,58,q},\n     {51,58,r},\n     {51,58,b},\n     {51,58,n},\n     {26,53},\n     {26,44},\n     {26,40},\n     {26,35},\n     {26,33},\n     {26,19},\n     {26,17},\n     {15,31},\n     {15,23},\n     {14,30},\n     {14,22},\n     {12,29},\n     {12,27},\n     {12,22},\n     {12,18},\n     {12,6},\n     {10,18},\n     {9,25},\n     {9,17},\n     {8,24},\n     {8,16},\n     {7,...},\n     {...}|...]}\n\n%% Count moves:\n\u003e erlang:length(Movelist).\n44\n\n\u003e binbo:all_legal_moves(Pid, bin).\n{ok,[{\u003c\u003c\"d7\"\u003e\u003e,\u003c\u003c\"c8\"\u003e\u003e,q},\n     {\u003c\u003c\"d7\"\u003e\u003e,\u003c\u003c\"c8\"\u003e\u003e,r},\n     {\u003c\u003c\"d7\"\u003e\u003e,\u003c\u003c\"c8\"\u003e\u003e,b},\n     {\u003c\u003c\"d7\"\u003e\u003e,\u003c\u003c\"c8\"\u003e\u003e,n},\n     {\u003c\u003c\"c4\"\u003e\u003e,\u003c\u003c\"f7\"\u003e\u003e},\n     {\u003c\u003c\"c4\"\u003e\u003e,\u003c\u003c\"e6\"\u003e\u003e},\n     {\u003c\u003c\"c4\"\u003e\u003e,\u003c\u003c\"a6\"\u003e\u003e},\n     {\u003c\u003c\"c4\"\u003e\u003e,\u003c\u003c\"d5\"\u003e\u003e},\n     {\u003c\u003c\"c4\"\u003e\u003e,\u003c\u003c\"b5\"\u003e\u003e},\n     {\u003c\u003c\"c4\"\u003e\u003e,\u003c\u003c\"d3\"\u003e\u003e},\n     {\u003c\u003c\"c4\"\u003e\u003e,\u003c\u003c\"b3\"\u003e\u003e},\n     {\u003c\u003c\"h2\"\u003e\u003e,\u003c\u003c\"h4\"\u003e\u003e},\n     {\u003c\u003c\"h2\"\u003e\u003e,\u003c\u003c\"h3\"\u003e\u003e},\n     {\u003c\u003c\"g2\"\u003e\u003e,\u003c\u003c\"g4\"\u003e\u003e},\n     {\u003c\u003c\"g2\"\u003e\u003e,\u003c\u003c\"g3\"\u003e\u003e},\n     {\u003c\u003c\"e2\"\u003e\u003e,\u003c\u003c\"f4\"\u003e\u003e},\n     {\u003c\u003c\"e2\"\u003e\u003e,\u003c\u003c\"d4\"\u003e\u003e},\n     {\u003c\u003c\"e2\"\u003e\u003e,\u003c\u003c\"g3\"\u003e\u003e},\n     {\u003c\u003c\"e2\"\u003e\u003e,\u003c\u003c\"c3\"\u003e\u003e},\n     {\u003c\u003c\"e2\"\u003e\u003e,\u003c\u003c\"g1\"\u003e\u003e},\n     {\u003c\u003c\"c2\"\u003e\u003e,\u003c\u003c\"c3\"\u003e\u003e},\n     {\u003c\u003c\"b2\"\u003e\u003e,\u003c\u003c\"b4\"\u003e\u003e},\n     {\u003c\u003c\"b2\"\u003e\u003e,\u003c\u003c\"b3\"\u003e\u003e},\n     {\u003c\u003c\"a2\"\u003e\u003e,\u003c\u003c\"a4\"\u003e\u003e},\n     {\u003c\u003c\"a2\"\u003e\u003e,\u003c\u003c...\u003e\u003e},\n     {\u003c\u003c...\u003e\u003e,...},\n     {...}|...]}\n\n\u003e binbo:all_legal_moves(Pid, str).\n{ok,[{\"d7\",\"c8\",q},\n     {\"d7\",\"c8\",r},\n     {\"d7\",\"c8\",b},\n     {\"d7\",\"c8\",n},\n     {\"c4\",\"f7\"},\n     {\"c4\",\"e6\"},\n     {\"c4\",\"a6\"},\n     {\"c4\",\"d5\"},\n     {\"c4\",\"b5\"},\n     {\"c4\",\"d3\"},\n     {\"c4\",\"b3\"},\n     {\"h2\",\"h4\"},\n     {\"h2\",\"h3\"},\n     {\"g2\",\"g4\"},\n     {\"g2\",\"g3\"},\n     {\"e2\",\"f4\"},\n     {\"e2\",\"d4\"},\n     {\"e2\",\"g3\"},\n     {\"e2\",\"c3\"},\n     {\"e2\",\"g1\"},\n     {\"c2\",\"c3\"},\n     {\"b2\",\"b4\"},\n     {\"b2\",\"b3\"},\n     {\"a2\",\"a4\"},\n     {\"a2\",[...]},\n     {[...],...},\n     {...}|...]}\n\n----\n\n=== Side to move\n\n[source,erlang]\n----\nbinbo:side_to_move(Pid) -\u003e {ok, white | black} | {error, Reason}.\n----\n\nIf White is to move, it returns `{ok, white}`. If Black is to move, it returns `{ok, black}`.\n\n.Example:\n[source,erlang]\n----\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.232.0\u003e}\n\n\u003e binbo:new_game(Pid).\n{ok,continue}\n\n\u003e binbo:side_to_move(Pid). % White is to move\n{ok,white}\n\n\u003e binbo:move(Pid, \u003c\u003c\"e2e4\"\u003e\u003e).\n{ok,continue}\n\n\u003e binbo:side_to_move(Pid). % Black is to move now\n{ok,black}\n----\n\n[[game-state]]\n=== Game state\n\n[source,erlang]\n----\nbinbo:game_state(Pid) -\u003e GameState.\nbinbo:set_game_state(Pid, GameState) -\u003e {ok, GameStatus} | {error, Reason}.\n----\n\n.where:\n* `Pid` is the pid of the game process;\n* `GameState` is the whole game state.\n* `GameStatus` is the link:#game-status[game status].\n\n`binbo:game_state/1` returns a *raw* game state, it may be useful when you want to save it somehow (e.g. into a database) and then restore it in the future with `binbo:set_game_state(Pid, GameState)`. It's much faster than restoring game move by move incrementally.\n\n.Example:\n[source,erlang]\n----\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.194.0\u003e}\n\n\u003e binbo:new_game(Pid).\n{ok,continue}\n\n\u003e GameState = binbo:game_state(Pid).\n#{12 =\u003e 1,4 =\u003e 6,38 =\u003e 0,16 =\u003e 0,53 =\u003e 17,46 =\u003e 0,28 =\u003e 0,\n  23 =\u003e 0,lastmovepc =\u003e 0,59 =\u003e 21,58 =\u003e 19,bbenpa =\u003e 0,\n  30 =\u003e 0,40 =\u003e 0,47 =\u003e 0,24 =\u003e 0,27 =\u003e 0,21 =\u003e 0,\n  bbwp =\u003e 65280,29 =\u003e 0,22 =\u003e 0,31 =\u003e 0,61 =\u003e 19,18 =\u003e 0,\n  54 =\u003e 17,5 =\u003e 3,14 =\u003e 1,51 =\u003e 17,57 =\u003e 18,...}\n\n\u003e BinGame = erlang:term_to_binary(GameState).\n\u003c\u003c131,116,0,0,0,89,97,48,97,17,100,0,4,98,98,98,98,110,8,\n  0,0,0,0,0,0,0,0,36,100,...\u003e\u003e\n\n\u003e binbo:set_game_state(Pid, erlang:binary_to_term(BinGame)).\n{ok,continue}\n----\n\n\n[[setting-a-draw]]\n=== Setting a draw\n\nIt is possible to set a draw via API:\n\n[source,erlang]\n----\nbinbo:game_draw(Pid) -\u003e ok | {error, Reason}.\nbinbo:game_draw(Pid, WhyDraw) -\u003e ok | {error, Reason}.\n----\n\n.where:\n* `Pid` is the pid of the game process;\n* `WhyDraw` is the reason why a draw is to be set.\n\nCalling `binbo:game_draw(Pid)` is the same as: `binbo:game_draw(Pid, undefined)`.\n\n.Example:\n[source,erlang]\n----\n% Players agreed to a draw:\n\u003e binbo:game_draw(Pid, by_agreement).\nok\n\n% Trying to set a draw for the other reason:\n\u003e binbo:game_draw(Pid, other_reason).\n{error,{already_has_status,{draw,{manual,by_agreement}}}}\n----\n\n\n[[setting-game-winner]]\n=== Setting game winner\n\n[source,erlang]\n----\nbinbo:set_game_winner(Pid, Winner) -\u003e ok | {error, Reason}.\nbinbo:set_game_winner(Pid, Winner, WinnerReason) -\u003e ok | {error, Reason}.\n----\n\n.where:\n* `Pid` is the pid of the game process;\n* `Winner` is the winner, it can be any Erlang term (`white`, `black`, `'Bobby Fischer'`, etc.);\n* `WinnerReason` is the reason why winner is to be set.\n\nCalling `binbo:set_game_winner(Pid, Winner)` is the same as: `binbo:set_game_winner(Pid, Winner, undefined)`.\n\n.Example:\n[source,erlang]\n----\n% Black resigned\n\u003e binbo:set_game_winner(Pid, white, black_resigned).\nok\n\n% Now the status of the game is: {winner,white,{manual,black_resigned}}\n\u003e binbo:game_status(Pid).\n{ok,{winner,white,{manual,black_resigned}}}\n\n% Trying to set the winner right after that (impossible):\n\u003e binbo:set_game_winner(Pid, white, black_lost_on_time).\n{error,{already_has_status,{winner,white,\n                                   {manual,black_resigned}}}}\n----\n\n\n=== Stopping game process\n\nIf, for some reason, you want to stop the game process and free resources, use:\n\n[source,erlang]\n----\nbinbo:stop_server(Pid) -\u003e ok | {error, {not_pid, Pid}}.\n----\n\nFunction terminates the game process with pid `Pid`.\n\n=== Stopping application\n\nTo stop Binbo, call:\n\n[source,erlang]\n----\nbinbo:stop().\n----\n\n=== Using chess engines\n\nYou can write a chess bot application or play with engine using functions described in this section.\n\n.Please note:\n* Chess engine must support UCI protocol;\n* Chess engine must be installed on the same machine where Binbo runs on.\n\nRead the https://gist.github.com/DOBRO/2592c6dad754ba67e6dcaec8c90165bf[description of the Universal Chess Interface (UCI)] with examples for details.\n\n[[start-new-game-with-engine]]\n==== Start new game with engine\n\n[source,erlang]\n----\nbinbo:new_uci_game(Pid, Options) -\u003e {ok, GameStatus} | {error, Reason}.\n----\n\n.Types:\n[source,erlang]\n----\nPid :: pid().\n\nOptions :: #{\n  engine_path := EnginePath,\n  fen =\u003e Fen\n}.\n\nEnginePath :: binary() | string() | {TCPHost, TCPPort, timeout()}.\nTCPHost :: inet:socket_address() | inet:hostname().\nTCPPort :: inet:port_number().\n\nFen :: binary() | string().\n----\n\n\n.where:\n* `Pid` is the `pid` of the process where the game is to be initialized;\n* `EnginePath` is the full path to the engine's executable file (e.g. `/usr/local/bin/stockfish`) or tuple `{Host, Port, Timeout}` for TCP connection;\n* `Fen` is the Forsyth–Edwards Notation (FEN), defaults to initial if omitted;\n* `GameStatus` is the link:#game-status[game status].\n\n\n.Example:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% Start new process for the game:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.185.0\u003e}\n\n% New game from the starting position:\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e \"/usr/local/bin/stockfish\"}).\n{ok,continue}\n\n% New game with the given FEN:\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e \"/usr/local/bin/stockfish\", fen =\u003e \u003c\u003c\"rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1\"\u003e\u003e}).\n{ok,continue}\n----\n\n[[search-for-the-best-move]]\n==== Search for the best move\n\n[source,erlang]\n----\nbinbo:uci_bestmove(Pid) -\u003e {ok, BestMove} | {error, Reason}.\nbinbo:uci_bestmove(Pid, BestMoveOptions) -\u003e {ok, BestMove} | {error, Reason}.\n----\n\n.Types:\n[source,erlang]\n----\nPid :: pid().\nBestMove :: binary() % e.g. \u003c\u003c\"e2e4\"\u003e\u003e, \u003c\u003c\"a7a8q\"\u003e\u003e, ...\n\nBestMoveOptions :: #{\n  depth  =\u003e pos_integer(),     % depth \u003cx\u003e (search x plies only)\n  wtime  =\u003e non_neg_integer(), % wtime \u003cx\u003e (white has x msec left on the clock)\n  btime  =\u003e non_neg_integer(), % btime \u003cx\u003e (black has x msec left on the clock)\n  winc  =\u003e pos_integer(),      % winc \u003cx\u003e (white increment per move in mseconds if x \u003e 0)\n  binc  =\u003e pos_integer(),      % binc \u003cx\u003e (black increment per move in mseconds if x \u003e 0)\n  movestogo =\u003e pos_integer(),  % movestogo \u003cx\u003e (there are x moves to the next time control, this will only be sent if x \u003e 0, if you don't get this and get the wtime and btime it's sudden death)\n  nodes  =\u003e pos_integer(),     % nodes \u003cx\u003e (search x nodes only)\n  movetime =\u003e pos_integer()    % movetime \u003cx\u003e (search exactly x mseconds)\n}.\n----\n\n`binbo:uci_bestmove(Pid)` is the same as `binbo:uci_bestmove(Pid, #{movetime =\u003e 1000})`, it sends command `go` to the engine.\n`binbo:uci_bestmove(Pid, BestMoveOptions)` sends command `go ...` to the engine adding values associated with the keys of `BestMoveOptions`.\n\nFor example, calling `binbo:uci_bestmove(Pid, #{movetime =\u0026gt; 2000, depth =\u0026gt; 10})` means sending command `go movetime 2000 depth 10` to the engine.\n\n**Note:** the very important option is `movetime`, it tells the engine how long (in milliseconds) to search for the best move. Defaults to **1000 milliseconds**.\n\nFunctions `binbo:uci_bestmove/2,3` do NOT change the position on the board, they return the bestmove as a hint. To make moves and play with engine, use functions link:#binbo-uci-play-docs[binbo:uci_play/2,3].\n\n.Example:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% Start new process for the game:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.185.0\u003e}\n\n% New game with the given FEN:\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e \"/usr/local/bin/stockfish\", fen =\u003e \u003c\u003c\"r1bqkbnr/pp1ppp1p/2n3p1/1Bp5/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 4\"\u003e\u003e}).\n{ok,continue}\n\n% Search for the best move (no options given):\n\u003e binbo:uci_bestmove(Pid).\n{ok,\u003c\u003c\"e1g1\"\u003e\u003e}\n\n% Search exactly 1000 milliseconds:\n\u003e binbo:uci_bestmove(Pid, #{movetime =\u003e 1000}).\n{ok,\u003c\u003c\"e1g1\"\u003e\u003e}\n\n% Search for the best move at depth 10:\n\u003e binbo:uci_bestmove(Pid, #{depth =\u003e 10}).\n{ok,\u003c\u003c\"b5c6\"\u003e\u003e}\n\n% Search exactly 5000 milliseconds at depth 30:\n\u003e binbo:uci_bestmove(Pid, #{depth =\u003e 30, movetime =\u003e 5000}).\n{ok,\u003c\u003c\"e1g1\"\u003e\u003e}\n----\n\n[[binbo-uci-play-docs]]\n==== Play with engine, make moves\n\n[source,erlang]\n----\nbinbo:uci_play(Pid, BestMoveOptions) -\u003e {ok, GameStatus, EngineMove} | {error, Reason}.\nbinbo:uci_play(Pid, BestMoveOptions, YourMove) -\u003e {ok, GameStatus, EngineMove} | {error, Reason}.\n----\n\n.where:\n* `Pid` - `pid` of the game process;\n* `BestMoveOptions` - options for the best move the engine should search for, same as options for link:#search-for-the-best-move[binbo:uci_bestmove/2];\n* `EngineMove` - move that was done by the engine;\n* `YourMove` - your move to send to the engine before it makes its move, e.g. `\u0026lt;\u0026lt;\"e2e4\"\u0026gt;\u0026gt;`, `\u0026lt;\u0026lt;\"a7a8q\"\u0026gt;\u0026gt;`, \u0026#8230;\n* `GameStatus` is the link:#game-status[game status].\n\nFunction `binbo:uci_play(Pid, BestMoveOptions)` goes through the following steps:\n\n* the engine searches for the bestmove (`EngineMove`) from the current position;\n* the engine makes this move and changes its internal position;\n* tuple `{ok, GameStatus, EngineMove}` is returned.\n\nThe behaviour of function `binbo:uci_play(Pid, BestMoveOptions, YourMove)` is slightly different. Here are the steps it goes through:\n\n* your move `YourMove` is sent to the engine;\n* the engine receives `YourMove` and changes its internal position;\n* the engine searches for the bestmove (`EngineMove`) from the changed position;\n* the engine makes this move and changes its internal position;\n* tuple `{ok, GameStatus, EngineMove}` is returned.\n\nSee how to play with engine in the link:#quickstart-play-with-engine[example] from _\u0026quot;Quick start\u0026quot;_ section.\n\n==== Сhange position after the game is created\n\n[source,erlang]\n----\nbinbo:uci_set_position(Pid, Fen) -\u003e {ok, GameStatus} | {error, Reason}.\n----\n\n.where:\n* `Pid` - `pid` of the game process;\n* `Fen` is the Forsyth–Edwards Notation (FEN);\n* `GameStatus` is the link:#game-status[game status].\n\nUsing this function you can change the position at any time. The game MUST be link:#start-new-game-with-engine[created] before.\n\n.Example:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% Start new process for the game:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.185.0\u003e}\n\n% Start new game from the initial position:\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e \"/usr/local/bin/stockfish\"}).\n\n% Set up new position with the given FEN:\n\u003e binbo:uci_set_position(Pid, \u003c\u003c\"r1bqk1nr/ppppppb1/2n3p1/7p/2PP4/5NPP/PP2PP2/RNBQKB1R b KQkq - 2 5\"\u003e\u003e).\n{ok,continue}\n----\n\n==== Synchronize positions\n\n[source,erlang]\n----\nbinbo:uci_sync_position(Pid) -\u003e ok | {error, Reason}.\n----\n\n.where:\n* `Pid` - `pid` of the game process.\n\nIt can be useful to call this function when the position of the game process was changed somehow and the engine wasn't notified about that.\n\n.Example:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% Start new process for the game:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.185.0\u003e}\n\n% Start new game from the initial position:\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e \"/usr/local/bin/stockfish\"}).\n\n% Make move (the engine knows nothing about it):\n\u003e binbo:move(Pid, \"e2e4\").\n{ok,continue}\n\n% Now synchronize the engine's position with the position of the game process:\n\u003e binbo:uci_sync_position(Pid).\nok\n----\n\n==== Send any command to engine\n\n[source,erlang]\n----\nbinbo:uci_command_call(Pid, Command) -\u003e ok | {error, Reason}.\nbinbo:uci_command_cast(Pid, Command) -\u003e ok.\n----\n\n.where:\n* `Pid` - `pid` of the game process;\n* `Command` - UCI command to send to the engine.\n\nYou can send any command to the engine with functions `binbo:uci_command_call/2` and `binbo:uci_command_cast/2`.\n\n`binbo:uci_command_call/2` is a synchronous function, it calls https://erlang.org/doc/man/gen_server.html#call-2[gen_server:call/2] inside. Returns `ok` if `Command` is sent, or tuple `{error, no_uci_connection}` if the engine's process is not connected to the game process.\n\n`binbo:uci_command_cast/2` is an asynchronous function, it calls https://erlang.org/doc/man/gen_server.html#cast-2[gen_server:cast/2] inside. Returns `ok`. It also checks if the engine's process is connected to the game process before sending message and, if not connected, returns `ok` anyway.\n\n.Example:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% Start new process for the game:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.185.0\u003e}\n\n% Start new game:\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e \"/usr/local/bin/stockfish\"}).\n{ok,continue}\n\n% Set hash to 32 MB (synchronous):\n\u003e binbo:uci_command_call(Pid, \"setoption name Hash value 32\").\nok\n\n% Set hash to 32 MB (asynchronous):\n\u003e binbo:uci_command_cast(Pid, \"setoption name Hash value 32\").\nok\n----\n\n==== Handling messages from engine\n\n[source,erlang]\n----\nbinbo:set_uci_handler(Pid, Handler) -\u003e ok.\n----\n\n.Types:\n[source,erlang]\n----\nPid :: pid().\nHandler :: undefined | default | fun().\n----\n\n.where:\n* `Pid` - `pid` of the game process;\n* `Handler` - what to do with the message received from the engine.\n\nIf `Handler` is `undefined`, no operations are performed (the initial behaviour).\n\nIf `Handler` is set to `default`, function `binbo_uci_protocol:default_handler/1` from module link:src/binbo_uci_protocol.erl[binbo_uci_protocol] is performed. It just prints the message to the Erlang shell.\n\nIf `Handler` is a **function of arity 1**, this function is performed. The only argument the function takes is the message received from the engine.\n\n**Note**: all the messages received from the engine are of `binary()` type.\n\n.Example with default handler:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% Start new process for the game:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.185.0\u003e}\n\n% Start new game (no message handler):\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e \"/usr/local/bin/stockfish\"}).\n{ok,continue}\n\n% Set default message handler:\n\u003e binbo:set_uci_handler(Pid, default).\nok\n\n% Now start new game (with default message handler):\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e \"/usr/local/bin/stockfish\"}).\n{ok,continue}\n----\n\n.\u0026#8230; and get the messages from the engine:\n[source]\n----\n--- UCI LOG BEGIN ---\nStockfish 10 64 POPCNT by T. Romstad, M. Costalba, J. Kiiski, G. Linscott\n--- UCI LOG END ---\n\n--- UCI LOG BEGIN ---\nid name Stockfish 10 64 POPCNT\nid author T. Romstad, M. Costalba, J. Kiiski, G. Linscott\n\noption name Debug Log File type string default\noption name Contempt type spin default 24 min -100 max 100\noption name Analysis Contempt type combo default Both var Off var White var Black var Both\noption name Threads type spin default 1 min 1 max 512\noption name Hash type spin default 16 min 1 max 131072\noption name Clear Hash type button\noption name Ponder type check default false\noption name MultiPV type spin default 1 min 1 max 500\noption name Skill Level type spin default 20 min 0 max 20\noption name Move Overhead type spin default 30 min 0 max 5000\noption name Minimum Thinking Time type spin default 20 min 0 max 5000\noption name Slow Mover type spin default 84 min 10 max 1000\noption name nodestime type spin default 0 min 0 max 10000\noption name UCI_Chess960 type check default false\noption name UCI_AnalyseMode type check default false\noption name SyzygyPath type string default \u003cempty\u003e\noption name SyzygyProbeDepth type spin default 1 min 1 max 100\noption name Syzygy50MoveRule type check default true\noption name SyzygyProbeLimit type spin default 7 min 0 max 7\nuciok\n--- UCI LOG END ---\n----\n\n.Example with custom message handler:\n[source,erlang]\n----\n%% In the Erlang shell.\n\n% Start new process for the game:\n\u003e {ok, Pid} = binbo:new_server().\n{ok,\u003c0.185.0\u003e}\n\n% Start new game (no message handler):\n\u003e binbo:new_uci_game(Pid, #{engine_path =\u003e \"/usr/local/bin/stockfish\"}).\n{ok,continue}\n\n% Remember pid of the calling process:\n\u003e SomePid = self().\n\u003c0.411.0\u003e\n\n% Set custom message handler as a function that resends messages to the process with pid SomePid:\n\u003e binbo:set_uci_handler(Pid, fun(Message) -\u003e SomePid ! Message end).\nok\n\n% Tell the engine to search for the bestmove:\n\u003e binbo:uci_bestmove(Pid).\n{ok,\u003c\u003c\"e2e4\"\u003e\u003e}\n\n% Get the messages received:\n\u003e flush().\nShell got \u003c\u003c\"info depth 1 seldepth 1 multipv 1 score cp 116 nodes 20 nps 20000 tbhits 0 time 1 pv e2e4\\n\"\u003e\u003e\nShell got \u003c\u003c\"info depth 2 seldepth 2 multipv 1 score cp 112 nodes 54 nps 54000 tbhits 0 time 1 pv e2e4 b7b6\\n\"\u003e\u003e\nShell got \u003c\u003c\"info depth 3 seldepth 3 multipv 1 score cp 148 nodes 136 nps 136000 tbhits 0 time 1 pv d2d4 d7d6 e2e4\\n\"\u003e\u003e\nShell got \u003c\u003c\"info depth 4 seldepth 4 multipv 1 score cp 137 nodes 247 nps 123500 tbhits 0 time 2 pv d2d4 e7e6 e2e4 c7c6\\n\"\u003e\u003e\nShell got \u003c\u003c\"info depth 5 seldepth 5 multipv 1 score cp 77 nodes 1157 nps 385666 tbhits 0 time 3 pv c2c3 d7d5 d2d4 b8c6 c1g5\\n\"\u003e\u003e\nShell got \u003c\u003c\"info depth 6 seldepth 6 multipv 1 score cp 83 nodes 2250 nps 562500 tbhits 0 time 4 pv e2e4 b8c6 d2d4 d7d6 f1c4 g8f6\\n\"\u003e\u003e\nShell got \u003c\u003c\"info depth 7 seldepth 7 multipv 1 score cp 67 nodes 4481 nps 746833 tbhits 0 time 6 pv e2e4 e7e5 d2d4 e5d4 d1d4 b8c6 d4d1\\n\"\u003e\u003e\nShell got \u003c\u003c\"info depth 8 seldepth 8 multipv 1 score cp 60 nodes 7849 nps 981125 tbhits 0 time 8 pv e2e4 e7e5 g1f3 d7d5 d2d4 b8c6 f3e5\\n\"\u003e\u003e\nShell got \u003c\u003c\"info depth 9 seldepth 11 multipv 1 score cp 115 nodes 11846 nps 1184600 tbhits 0 time 10 pv e2e4 e7e5 g1f3 g8f6 b1c3\\n\"\u003e\u003e\nShell got \u003c\u003c\"info depth 10 seldepth 10 multipv 1 score cp 106 upperbound nodes 14951 nps 1245916 tbhits 0 time 12 pv e2e4 d7d5\\nbestmove e2e4 ponder d7d5\\n\"\u003e\u003e\nok\n\n% Now turn the message handler off:\n\u003e binbo:set_uci_handler(Pid, undefined).\nok\n----\n\n\n=== Other helper functions\n\n==== binbo:get_pieces_list/2\n\n[source,erlang]\n----\nbinbo:get_pieces_list(Pid, SquareType) -\u003e {ok, PiecesList} | {error, Reason}.\n----\n\n.where:\n* `Pid` - `pid` of the game process;\n* `SquareType` is one of the atoms: `index` or `notation`;\n* `PiecesList` - list of tuples `{Square, Color, PieceType}`:\n** `Square` - square index (`0 .. 63`) or notation (`binary`: `\u003c\u003c\"a1\"\u003e\u003e`, ..., `\u003c\u003c\"h8\"\u003e\u003e`) depending on `SquareType`;\n** `Color` - `white` | `black`;\n** `PieceType` - `pawn` | `knight` | `bishop` | `rook` | `queen` | `king`.\n\n.Example:\n[source,erlang]\n----\n\u003e binbo:get_pieces_list(Pid, index).\n{ok,[{63,black,rook},\n     {62,black,knight},\n     {61,black,bishop},\n     {60,black,king},\n     {59,black,queen},\n     {58,black,bishop},\n     {57,black,knight},\n     {56,black,rook},\n     {55,black,pawn},\n     {54,black,pawn},\n     {53,black,pawn},\n     {52,black,pawn},\n     {51,black,pawn},\n     {50,black,pawn},\n     {49,black,pawn},\n     {48,black,pawn},\n     {15,white,pawn},\n     {14,white,pawn},\n     {13,white,pawn},\n     {12,white,pawn},\n     {11,white,pawn},\n     {10,white,pawn},\n     {9,white,pawn},\n     {8,white,pawn},\n     {7,white,...},\n     {6,...},\n     {...}|...]}\n\n\u003e binbo:get_pieces_list(Pid, notation).\n{ok,[{\u003c\u003c\"h8\"\u003e\u003e,black,rook},\n     {\u003c\u003c\"g8\"\u003e\u003e,black,knight},\n     {\u003c\u003c\"f8\"\u003e\u003e,black,bishop},\n     {\u003c\u003c\"e8\"\u003e\u003e,black,king},\n     {\u003c\u003c\"d8\"\u003e\u003e,black,queen},\n     {\u003c\u003c\"c8\"\u003e\u003e,black,bishop},\n     {\u003c\u003c\"b8\"\u003e\u003e,black,knight},\n     {\u003c\u003c\"a8\"\u003e\u003e,black,rook},\n     {\u003c\u003c\"h7\"\u003e\u003e,black,pawn},\n     {\u003c\u003c\"g7\"\u003e\u003e,black,pawn},\n     {\u003c\u003c\"f7\"\u003e\u003e,black,pawn},\n     {\u003c\u003c\"e7\"\u003e\u003e,black,pawn},\n     {\u003c\u003c\"d7\"\u003e\u003e,black,pawn},\n     {\u003c\u003c\"c7\"\u003e\u003e,black,pawn},\n     {\u003c\u003c\"b7\"\u003e\u003e,black,pawn},\n     {\u003c\u003c\"a7\"\u003e\u003e,black,pawn},\n     {\u003c\u003c\"h2\"\u003e\u003e,white,pawn},\n     {\u003c\u003c\"g2\"\u003e\u003e,white,pawn},\n     {\u003c\u003c\"f2\"\u003e\u003e,white,pawn},\n     {\u003c\u003c\"e2\"\u003e\u003e,white,pawn},\n     {\u003c\u003c\"d2\"\u003e\u003e,white,pawn},\n     {\u003c\u003c\"c2\"\u003e\u003e,white,pawn},\n     {\u003c\u003c\"b2\"\u003e\u003e,white,pawn},\n     {\u003c\u003c\"a2\"\u003e\u003e,white,pawn},\n     {\u003c\u003c\"h1\"\u003e\u003e,white,...},\n     {\u003c\u003c...\u003e\u003e,...},\n     {...}|...]}\n----\n\n\n== Building and testing\n\nTwo possible ways are presented here for building and testing the application (with `make` and `rebar3`).\n\n=== Building\n\n[source,bash]\n----\n$ make\n----\n\n[source,bash]\n----\n$ rebar3 compile\n----\n\n=== Dialyzer\n\n[source,bash]\n----\n$ make dialyze\n----\n\n[source,bash]\n----\n$ rebar3 dialyzer\n----\n\n=== Testing\n\n[source,bash]\n----\n$ make test\n\n$ export BINBO_UCI_ENGINE_PATH=\"/path/to/engine\"\n$ export BINBO_UCI_ENGINE_HOST=localhost\n$ export BINBO_UCI_ENGINE_PORT=9010\n$ make test\n----\n\n[source,bash]\n----\n$ rebar3 ct --verbose\n\n$ export BINBO_UCI_ENGINE_PATH=\"/path/to/engine\"\n$ export BINBO_UCI_ENGINE_HOST=localhost\n$ export BINBO_UCI_ENGINE_PORT=9010\n$ rebar3 ct --verbose\n----\n\n=== Code coverage\n\n[source,bash]\n----\n$ make cover\n----\n\n[source,bash]\n----\n$ rebar3 cover\n----\n\n=== Generating Edoc files\n\n[source,bash]\n----\n$ make docs\n----\n\n[source,bash]\n----\n$ rebar3 edoc\n----\n\n\n== Binbo and Magic Bitboards\n\nAs mentioned above, Binbo uses https://www.chessprogramming.org/Magic_Bitboards[Magic Bitboards], the fastest solution for move generation of sliding pieces\n(rook, bishop, and queen). Good explanations of this approach can also be found https://stackoverflow.com/questions/16925204/sliding-move-generation-using-magic-bitboard/30862064#30862064[here]\nand http://vicki-chess.blogspot.com/2013/04/magics.html[here].\n\nThe main problem is to find the _index_ which is then used to lookup legal moves\nof sliding pieces in a preinitialized move database.\nThe formula for the _index_ is:\n\n._in C/C++:_\n[source,c]\n----\nmagic_index = ((occupied \u0026 mask) * magic_number) \u003e\u003e shift;\n----\n\n._in Erlang:_\n[source,erlang]\n----\nMagicIndex = (((Occupied band Mask) * MagicNumber) bsr Shift).\n----\n\n._where:_\n* `Occupied` is the bitboard of all pieces.\n* `Mask` is the attack mask of a piece for a given square.\n* `MagicNumber` is the magic number, see \u0026quot;https://www.chessprogramming.org/Looking_for_Magics[Looking for Magics]\u0026quot;.\n* `Shift = (64 - Bits)`, where `Bits` is the number of bits corresponding to attack mask of a given square.\n\nAll values for _magic numbers_ and _shifts_ are precalculated before and stored in `binbo_magic.hrl`.\n\nTo be accurate, Binbo uses https://www.chessprogramming.org/Magic_Bitboards#Fancy[Fancy Magic Bitboards].\nIt means that all moves are stored in a table of its own (individual) size for each square.\nIn _C/C++_ such tables are actually two-dimensional arrays and any move can be accessed by\na simple lookup:\n\n[source,c]\n----\nmove = global_move_table[square][magic_index]\n----\n\n._If detailed:_\n[source,c]\n----\nmoves_from = global_move_table[square];\nmove = moves_from[magic_index];\n----\n\nThe size of `moves_from` table depends on piece and square where it is placed on. For example:\n\n* for rook on `A1` the size of `moves_from` is `4096` (2^12 = 4096, 12 bits required for the attack mask);\n* for bishop on `A1` it is `64` (2^6 = 64, 6 bits required for the attack mask).\n\nThere are no two-dimensional arrays in Erlang, and no global variables which could help us\nto get the fast access to the move tables **from everywhere**.\n\nSo, how does Binbo beat this? Well, it's simple :\u0026#41;.\n\nErlang gives us the power of _tuples_ and _maps_ with their blazing fast lookup of _elements/values_ by their _index/key_.\n\nSince the number of squares on the chessboard is the constant value (it's always **64**, right?),\nour `global_move_table` can be constructed as a _tuple_ of 64 elements, and each element of this _tuple_\nis a _map_ containing the _key-value_ association as `MagicIndex =\u0026gt; Moves`.\n\n._If detailed, for moves:_\n[source,erlang]\n----\nGlobalMovesTable = { MoveMap1, ..., MoveMap64 }\n----\n\n._where:_\n[source,erlang]\n----\nMoveMap1  = #{\n  MagicIndex_1_1 =\u003e Moves_1_1,\n  ...\n  MagicIndex_1_K =\u003e Moves_1_K\n},\nMoveMap64 = #{\n  MagicIndex_64_1 =\u003e Moves_64_1, ...\n  ...\n  MagicIndex_64_N =\u003e Moves_64_N\n},\n----\n\nand then we lookup legal moves from a square, say, `E4` (29th element of the _tuple_):\n\n[source,erlang]\n----\nE4 = 29,\nMoveMapE4   = erlang:element(E4, GlobalMovesTable),\nMovesFromE4 = maps:get(MagicIndex, MovesMapE4).\n----\n\nTo calculate _magic index_ we also need the _attack mask_ for a given square.\nEvery _attack mask_ generated is stored in a _tuple_ of 64 elements:\n\n[source,erlang]\n----\nGlobalMaskTable = {Mask1, Mask2, ..., Mask64}\n----\n\nwhere `Mask1`, `Mask2`, ..., `Mask64` are _bitboards_ (integers).\n\nFinally, if we need to get all moves from `E4`:\n\n[source,erlang]\n----\nE4 = 29,\nMask = erlang:element(E4, GlobalMaskTable),\nMagicIndex = ((Occupied band Mask) * MagicNumber) bsr Shift,\nMoveMapE4   = erlang:element(E4, GlobalMovesTable),\nMovesFromE4 = maps:get(MagicIndex, MovesMapE4).\n----\n\nNext, no global variables? We make them global!\n\nHow do we get the fastest access to the _move tables_ and to the _attack masks_ **from everywhere**?\n\nhttp://erlang.org/doc/man/ets.html[ETS]? No! Using ETS as a storage for _static terms_ we get the overhead due to extra data copying during lookup.\n\nAnd now we are coming to the fastest solution.\n\nWhen Binbo starts up, all _move tables_ are initialized.\nOnce these tables (_tuples_, actually) initialized, they are \"injected\" into **dynamically generated\nmodules compiled at Binbo start**. Then, to get the values, we just call a _getter function_\n(`binbo_global:get/1`) with the argument as the name of the corresponding dynamic module.\n\nThis awesome trick is used in MochiWeb library, see module https://github.com/mochi/mochiweb/blob/master/src/mochiglobal.erl[mochiglobal].\n\nUsing http://erlang.org/doc/man/persistent_term.html[persistent_term] (since OTP 21.2) for storing static data is also a good idea.\nBut it doesn't seem to be a better way for the following reason with respect to dynamic modules.\nWhen Binbo stops, it gets them **unloaded** as they are not necessary anymore.\nIt should do the similar things for `persistent_term` data, say, delete all _unused\nterms_ to free memory.\nIn this case we run into the issue regarding scanning the _heaps_ in all processes.\n\nSo, using `global` dynamic modules with large static data seems to be more reasonable in spite of that fact that it significantly slows down the application startup due to the run-time compilation of these modules.\n\n== Changelog\n\nSee link:CHANGELOG.md[CHANGELOG] for details.\n\n== Contributing\n\nWant to contribute? Really? Awesome!\n\nPlease refer to the link:CONTRIBUTING.md[CONTRIBUTING] file for details.\n\n== License\n\nThis project is licensed under the terms of the Apache License, Version 2.0.\n\nSee the link:LICENSE[LICENSE] file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDOBRO%2Fbinbo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDOBRO%2Fbinbo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDOBRO%2Fbinbo/lists"}