Packages

A Gleam wrapper for the Battlesnake API

Current section

Files

Jump to
battlesnake src battlesnake.erl
Raw

src/battlesnake.erl

-module(battlesnake).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([move/1, shout/2, color_from_rgb/3, color_from_hex/1, color_to_string/1, config/0, with_author/2, with_color/2, with_head/2, with_tail/2, with_version/2, simple/2, stateful/4]).
-export_type([direction/0, move/0, color/0, snake_config/0, game_message/0]).
-type direction() :: up | down | left | right.
-type move() :: {move, direction(), gleam@option:option(binary())}.
-opaque color() :: {color, integer(), integer(), integer()}.
-type snake_config() :: {snake_config,
binary(),
gleam@option:option(binary()),
gleam@option:option(color()),
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(binary())}.
-type game_message() :: {game_move,
gleam@erlang@process:subject(move()),
battlesnake@gamestate:game_state()} |
{game_end, battlesnake@gamestate:game_state()}.
-spec direction_to_string(direction()) -> binary().
direction_to_string(Direction) ->
case Direction of
up ->
<<"up"/utf8>>;
down ->
<<"down"/utf8>>;
left ->
<<"left"/utf8>>;
right ->
<<"right"/utf8>>
end.
-spec move(direction()) -> move().
move(Direction) ->
{move, Direction, none}.
-spec shout(move(), binary()) -> move().
shout(Move, Shout) ->
erlang:setelement(3, Move, {some, Shout}).
-spec move_to_json(move()) -> gleam@json:json().
move_to_json(Move) ->
gleam@json:object(
[{<<"move"/utf8>>,
gleam@json:string(
begin
_pipe = erlang:element(2, Move),
direction_to_string(_pipe)
end
)},
{<<"shout"/utf8>>,
gleam@json:nullable(
begin
_pipe@1 = erlang:element(3, Move),
gleam@option:map(
_pipe@1,
fun(_capture) ->
gleam@string:slice(_capture, 0, 256)
end
)
end,
fun gleam@json:string/1
)}]
).
-spec color_from_rgb(integer(), integer(), integer()) -> {ok, color()} |
{error, nil}.
color_from_rgb(Red, Green, Blue) ->
gleam@bool:guard(
(((((Red < 0) orelse (Red >= 256)) orelse (Green < 0)) orelse (Green >= 256))
orelse (Blue < 0))
orelse (Blue >= 256),
{error, nil},
fun() ->
Color = {color, Red, Green, Blue},
{ok, Color}
end
).
-spec color_from_hex(binary()) -> {ok, color()} | {error, nil}.
color_from_hex(Color) ->
gleam@result:'try'(case {Color, gleam@string:length(Color)} of
{<<"#"/utf8, Rest/binary>>, 7} ->
{ok, Rest};
{_, 6} ->
{ok, Color};
{_, _} ->
{error, nil}
end, fun(Hex) ->
gleam@result:'try'(
begin
_pipe = gleam@string:slice(Hex, 0, 2),
gleam@int:base_parse(_pipe, 16)
end,
fun(Red) ->
gleam@result:'try'(
begin
_pipe@1 = gleam@string:slice(Hex, 2, 2),
gleam@int:base_parse(_pipe@1, 16)
end,
fun(Green) ->
gleam@result:'try'(
begin
_pipe@2 = gleam@string:slice(Hex, 4, 2),
gleam@int:base_parse(_pipe@2, 16)
end,
fun(Blue) ->
color_from_rgb(Red, Green, Blue)
end
)
end
)
end
)
end).
-spec color_to_string(color()) -> binary().
color_to_string(Color) ->
Red_string = begin
_pipe = gleam@int:to_base16(erlang:element(2, Color)),
gleam@string:pad_left(_pipe, 2, <<"0"/utf8>>)
end,
Green_string = begin
_pipe@1 = gleam@int:to_base16(erlang:element(3, Color)),
gleam@string:pad_left(_pipe@1, 2, <<"0"/utf8>>)
end,
Blue_string = begin
_pipe@2 = gleam@int:to_base16(erlang:element(4, Color)),
gleam@string:pad_left(_pipe@2, 2, <<"0"/utf8>>)
end,
<<<<<<"#"/utf8, Red_string/binary>>/binary, Green_string/binary>>/binary,
Blue_string/binary>>.
-spec config() -> snake_config().
config() ->
{snake_config, <<"1"/utf8>>, none, none, none, none, none}.
-spec with_author(snake_config(), binary()) -> snake_config().
with_author(Battlesnake, Author) ->
erlang:setelement(3, Battlesnake, {some, Author}).
-spec with_color(snake_config(), color()) -> snake_config().
with_color(Battlesnake, Color) ->
erlang:setelement(4, Battlesnake, {some, Color}).
-spec with_head(snake_config(), binary()) -> snake_config().
with_head(Battlesnake, Head) ->
erlang:setelement(5, Battlesnake, {some, Head}).
-spec with_tail(snake_config(), binary()) -> snake_config().
with_tail(Battlesnake, Tail) ->
erlang:setelement(6, Battlesnake, {some, Tail}).
-spec with_version(snake_config(), binary()) -> snake_config().
with_version(Battlesnake, Version) ->
erlang:setelement(7, Battlesnake, {some, Version}).
-spec config_to_json(snake_config()) -> gleam@json:json().
config_to_json(Battlesnake) ->
gleam@json:object(
[{<<"apiversion"/utf8>>,
gleam@json:string(erlang:element(2, Battlesnake))},
{<<"author"/utf8>>,
gleam@json:nullable(
erlang:element(3, Battlesnake),
fun gleam@json:string/1
)},
{<<"color"/utf8>>,
gleam@json:nullable(
begin
_pipe = erlang:element(4, Battlesnake),
gleam@option:map(_pipe, fun color_to_string/1)
end,
fun gleam@json:string/1
)},
{<<"head"/utf8>>,
gleam@json:nullable(
erlang:element(5, Battlesnake),
fun gleam@json:string/1
)},
{<<"tail"/utf8>>,
gleam@json:nullable(
erlang:element(6, Battlesnake),
fun gleam@json:string/1
)},
{<<"version"/utf8>>,
gleam@json:nullable(
erlang:element(7, Battlesnake),
fun gleam@json:string/1
)}]
).
-spec simple(
snake_config(),
fun((battlesnake@gamestate:game_state()) -> move())
) -> fun((gleam@http@request:request(wisp@internal:connection())) -> gleam@http@response:response(wisp:body())).
simple(Config, Callback) ->
fun(Request) -> case fun gleam@http@request:path_segments/1(Request) of
[] ->
_pipe = Config,
_pipe@1 = config_to_json(_pipe),
_pipe@2 = gleam@json:to_string_builder(_pipe@1),
wisp:json_response(_pipe@2, 200);
[<<"start"/utf8>>] ->
wisp:require_method(
Request,
post,
fun() ->
wisp:require_json(
Request,
fun(Json) ->
case battlesnake@gamestate:decode(Json) of
{ok, _} ->
wisp:ok();
{error, _} ->
wisp:bad_request()
end
end
)
end
);
[<<"move"/utf8>>] ->
wisp:require_method(
Request,
post,
fun() ->
wisp:require_json(
Request,
fun(Json@1) ->
case battlesnake@gamestate:decode(Json@1) of
{error, _} ->
wisp:bad_request();
{ok, Gamestate} ->
_pipe@3 = Callback(Gamestate),
_pipe@4 = move_to_json(_pipe@3),
_pipe@5 = gleam@json:to_string_builder(
_pipe@4
),
wisp:json_response(_pipe@5, 200)
end
end
)
end
);
[<<"end"/utf8>>] ->
wisp:require_method(
Request,
post,
fun() ->
wisp:require_json(
Request,
fun(Json@2) ->
case battlesnake@gamestate:decode(Json@2) of
{ok, _} ->
wisp:ok();
{error, _} ->
wisp:bad_request()
end
end
)
end
);
_ ->
wisp:not_found()
end end.
-spec create_game_handler(
fun((battlesnake@gamestate:game_state(), TYD) -> {move(), TYD}),
fun((battlesnake@gamestate:game_state(), TYD) -> nil)
) -> fun((game_message(), TYD) -> gleam@otp@actor:next(game_message(), TYD)).
create_game_handler(Move, End) ->
fun(Message, State) -> case Message of
{game_move, Client, Gamestate} ->
{Next_move, Next_state} = Move(Gamestate, State),
gleam@erlang@process:send(Client, Next_move),
gleam@otp@actor:continue(Next_state);
{game_end, Gamestate@1} ->
End(Gamestate@1, State),
{stop, normal}
end end.
-spec 'try'({ok, TYG} | {error, any()}, fun(() -> TYK), fun((TYG) -> TYK)) -> TYK.
'try'(Result, On_error, Fun) ->
case Result of
{error, _} ->
On_error();
{ok, Ok} ->
Fun(Ok)
end.
-spec stateful(
snake_config(),
fun((battlesnake@gamestate:game_state()) -> TYL),
fun((battlesnake@gamestate:game_state(), TYL) -> {move(), TYL}),
fun((battlesnake@gamestate:game_state(), TYL) -> nil)
) -> fun((gleam@http@request:request(wisp@internal:connection())) -> gleam@http@response:response(wisp:body())).
stateful(Config, Start, Move, End) ->
Games_cache = battlesnake@internal@cache:new(),
Game_handler = create_game_handler(Move, End),
fun(Request) -> case fun gleam@http@request:path_segments/1(Request) of
[] ->
_pipe = Config,
_pipe@1 = config_to_json(_pipe),
_pipe@2 = gleam@json:to_string_builder(_pipe@1),
wisp:json_response(_pipe@2, 200);
[<<"start"/utf8>>] ->
wisp:require_method(
Request,
post,
fun() ->
wisp:require_json(
Request,
fun(Json) ->
'try'(
battlesnake@gamestate:decode(Json),
fun wisp:bad_request/0,
fun(Gamestate) ->
Initial_state = Start(Gamestate),
'try'(
gleam@otp@actor:start(
Initial_state,
Game_handler
),
fun wisp:internal_server_error/0,
fun(Actor) ->
Game_id = erlang:element(
2,
erlang:element(2, Gamestate)
),
battlesnake@internal@cache:insert(
Games_cache,
Game_id,
Actor
),
wisp:ok()
end
)
end
)
end
)
end
);
[<<"move"/utf8>>] ->
wisp:require_method(
Request,
post,
fun() ->
wisp:require_json(
Request,
fun(Json@1) ->
'try'(
battlesnake@gamestate:decode(Json@1),
fun wisp:bad_request/0,
fun(Gamestate@1) ->
Game_id@1 = erlang:element(
2,
erlang:element(2, Gamestate@1)
),
'try'(
battlesnake@internal@cache:get(
Games_cache,
Game_id@1
),
fun wisp:bad_request/0,
fun(Actor@1) ->
'try'(
gleam@erlang@process:try_call(
Actor@1,
fun(_capture) ->
{game_move,
_capture,
Gamestate@1}
end,
500
),
fun wisp:internal_server_error/0,
fun(Move@1) ->
_pipe@3 = Move@1,
_pipe@4 = move_to_json(
_pipe@3
),
_pipe@5 = gleam@json:to_string_builder(
_pipe@4
),
wisp:json_response(
_pipe@5,
200
)
end
)
end
)
end
)
end
)
end
);
[<<"end"/utf8>>] ->
wisp:require_method(
Request,
post,
fun() ->
wisp:require_json(
Request,
fun(Json@2) ->
'try'(
battlesnake@gamestate:decode(Json@2),
fun wisp:bad_request/0,
fun(Gamestate@2) ->
Game_id@2 = erlang:element(
2,
erlang:element(2, Gamestate@2)
),
'try'(
battlesnake@internal@cache:get(
Games_cache,
Game_id@2
),
fun wisp:bad_request/0,
fun(Actor@2) ->
gleam@erlang@process:send(
Actor@2,
{game_end, Gamestate@2}
),
battlesnake@internal@cache:remove(
Games_cache,
Game_id@2
),
wisp:ok()
end
)
end
)
end
)
end
);
_ ->
wisp:not_found()
end end.