Pong bot for erlang.
This commit is contained in:
parent
5620b67c34
commit
9986a801aa
2 changed files with 305 additions and 0 deletions
123
games/pong_bot_e/ggs_network.erl
Normal file
123
games/pong_bot_e/ggs_network.erl
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
-module(ggs_network).
|
||||||
|
-export([connect/0,append_key_value_strings_to_dict/2,key_value_string_to_list/1]).
|
||||||
|
|
||||||
|
connect() ->
|
||||||
|
{ok,Socket} = gen_tcp:connect("localhost", 9000,[{active, false}]),
|
||||||
|
A = gen_tcp:recv(Socket,0),
|
||||||
|
read(A),
|
||||||
|
Socket.
|
||||||
|
|
||||||
|
read(Message) ->
|
||||||
|
case Message of
|
||||||
|
{ok, M} ->
|
||||||
|
HeaderList = string:tokens(M, "\n"),
|
||||||
|
Headers = extract_headers(HeaderList),
|
||||||
|
Data = extract_data(HeaderList),
|
||||||
|
received_command(Headers, Data)
|
||||||
|
end.
|
||||||
|
|
||||||
|
received_command(Headers, Data) ->
|
||||||
|
{ok, CommandList} = dict:find("Client-Command", Headers),
|
||||||
|
Command = lists:nth(1, CommandList),
|
||||||
|
case Command of
|
||||||
|
"hello" ->
|
||||||
|
io:format("Received command 'hello'~n"),
|
||||||
|
pong_bot:set_game_token(Data),
|
||||||
|
%gen_server:cast({global, pong_bot}, {game_token, Data}),
|
||||||
|
send_command("Ready", "");
|
||||||
|
%pong_bot:ggsNetworkReady(); Unneccessary
|
||||||
|
"defined" ->
|
||||||
|
ok;
|
||||||
|
%pong_bot:ggsNetworkDefined(); Unneccessary
|
||||||
|
Command ->
|
||||||
|
gen_server:ggsNetworkReceivedCommandWithArgs(Command, Data)
|
||||||
|
end.
|
||||||
|
|
||||||
|
make_message(ServerOrGame, Command, Args) ->
|
||||||
|
io:format("Make message~n"),
|
||||||
|
GameToken = pong_bot:get_game_token(),
|
||||||
|
% GameToken = gen_server:call({global, pong_bot}, game_token),
|
||||||
|
io:format("Make message2~n"),
|
||||||
|
StrGameToken = string:concat("Token: ", GameToken),
|
||||||
|
StrGameTokenln = string:concat(StrGameToken, "\n"),
|
||||||
|
StrCommand = string:concat("-Command: ", Command),
|
||||||
|
StrCommandln = string:concat(StrCommand, "\n"),
|
||||||
|
StrFullCommand = string:concat(ServerOrGame, StrCommandln),
|
||||||
|
StrContentLength = string:concat("Content-Length: ", integer_to_list(length(Args))),
|
||||||
|
StrContentLengthln = string:concat(StrContentLength, "\n\n"),
|
||||||
|
StrTokenCommand = string:concat(StrGameTokenln, StrFullCommand),
|
||||||
|
Message = string:concat(StrTokenCommand, StrContentLengthln),
|
||||||
|
|
||||||
|
MessageWithArgs = string:concat(Message, list_concat(Args,[])),
|
||||||
|
MessageWithArgs.
|
||||||
|
|
||||||
|
%define(SourceCode) ->
|
||||||
|
% write(make_message("Server", "define", SourceCode)).
|
||||||
|
|
||||||
|
|
||||||
|
send_command(Command, Args) ->
|
||||||
|
write(make_message("Client", Command, Args)).
|
||||||
|
|
||||||
|
write(Message) ->
|
||||||
|
Socket = gen_server:call({global, pong_bot}, socket),
|
||||||
|
gen_tcp:send(Socket, Message).
|
||||||
|
|
||||||
|
list_concat([],Ret) ->
|
||||||
|
Ret;
|
||||||
|
list_concat([E|ES],Ret) ->
|
||||||
|
NewRet = string:concat(Ret,E),
|
||||||
|
list_concat(ES,NewRet).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%%Packet parsing.%%%
|
||||||
|
|
||||||
|
|
||||||
|
extract_headers(Source) ->
|
||||||
|
key_value_strings_to_dict(Source).
|
||||||
|
|
||||||
|
|
||||||
|
extract_data([]) ->
|
||||||
|
[];
|
||||||
|
extract_data([E|ES]) ->
|
||||||
|
KeyValueList = key_value_string_to_list(E),
|
||||||
|
case length(KeyValueList) of
|
||||||
|
2 ->
|
||||||
|
extract_data(ES);
|
||||||
|
_ ->
|
||||||
|
E
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
%%%Low-level internals.%%%
|
||||||
|
|
||||||
|
|
||||||
|
%%["K1: V1","K2: V2","KN: VN" ...] -> Dict
|
||||||
|
key_value_strings_to_dict(Strings) ->
|
||||||
|
Dict = dict:new(),
|
||||||
|
append_key_value_strings_to_dict(Strings,Dict).
|
||||||
|
|
||||||
|
%%["K1: V1","K2: V2","KN: VN" ...], Dict -> NewDict
|
||||||
|
append_key_value_strings_to_dict([Str|Strings],Dict) ->
|
||||||
|
KeyValueList = key_value_string_to_list(Str),
|
||||||
|
case length(KeyValueList) of
|
||||||
|
2 ->
|
||||||
|
NewDict = append_string_pair_to_dict(Dict,lists:nth(1,KeyValueList),lists:nth(2,KeyValueList)),
|
||||||
|
append_key_value_strings_to_dict(Strings,NewDict);
|
||||||
|
_ ->
|
||||||
|
append_key_value_strings_to_dict(Strings,Dict)
|
||||||
|
end;
|
||||||
|
append_key_value_strings_to_dict([],Dict) ->
|
||||||
|
Dict.
|
||||||
|
|
||||||
|
|
||||||
|
%%"Hello: "World!" -> ["Hello","World!"]
|
||||||
|
key_value_string_to_list(KeyValueString) ->
|
||||||
|
string:tokens(KeyValueString, ": ").
|
||||||
|
|
||||||
|
|
||||||
|
%%Append a key str1 and a value str2 to the dict Dict
|
||||||
|
append_string_pair_to_dict(Dict, Str1, Str2) ->
|
||||||
|
dict:append(Str1, Str2, Dict).
|
||||||
|
|
||||||
|
|
182
games/pong_bot_e/pong_bot.erl
Normal file
182
games/pong_bot_e/pong_bot.erl
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
-module(pong_bot).
|
||||||
|
-behaviour(gen_server).
|
||||||
|
-export([start_link/0]).
|
||||||
|
-export([init/1, handle_call/3, handle_cast/2]).
|
||||||
|
-export([ggsNetworkReceivedCommandWithArgs/2,set_game_token/1,get_game_token/0]).
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
gen_server:start_link({global, pong_bot}, pong_bot, [], []).
|
||||||
|
|
||||||
|
|
||||||
|
init(_Args) ->
|
||||||
|
Player1 = new_pos(),
|
||||||
|
Player2 = new_pos(),
|
||||||
|
Ball = new_pos(),
|
||||||
|
Paused = true,
|
||||||
|
SendStart = false,
|
||||||
|
GGSNetwork = ggs_network:connect(), %Localhost is set internally inside
|
||||||
|
%ggs_network.
|
||||||
|
State1 = dict:new(),
|
||||||
|
State2 = dict:store(player1, Player1, State1),
|
||||||
|
State3 = dict:store(player2, Player2, State2),
|
||||||
|
State4 = dict:store(ball, Ball, State3),
|
||||||
|
State5 = dict:store(paused, Paused, State4),
|
||||||
|
State6 = dict:store(send_start, SendStart, State5),
|
||||||
|
State = dict:store(ggs_network, GGSNetwork, State6),
|
||||||
|
State.
|
||||||
|
|
||||||
|
new_pos() ->
|
||||||
|
{0, 0}.
|
||||||
|
|
||||||
|
|
||||||
|
ggsNetworkReceivedCommandWithArgs(Command, Args) ->
|
||||||
|
case Command of
|
||||||
|
"welcome" ->
|
||||||
|
welcome(Args);
|
||||||
|
"ball" ->
|
||||||
|
ball(Args);
|
||||||
|
"player1_y" ->
|
||||||
|
player1_y(Args);
|
||||||
|
"player2_y" ->
|
||||||
|
player2_y(Args);
|
||||||
|
"game" ->
|
||||||
|
game(Args);
|
||||||
|
"player1_points" ->
|
||||||
|
new_round();
|
||||||
|
"player2_points" ->
|
||||||
|
new_round()
|
||||||
|
end.
|
||||||
|
|
||||||
|
welcome(Who_am_I) ->
|
||||||
|
case Who_am_I of
|
||||||
|
1 ->
|
||||||
|
Me = gen_server:call(pong_bot, player1),
|
||||||
|
gen_server:cast(pong_bot, {me, Me});
|
||||||
|
2 ->
|
||||||
|
Me = gen_server:call(pong_bot, player2),
|
||||||
|
gen_server:cast(pong_bot, {me, Me})
|
||||||
|
end,
|
||||||
|
|
||||||
|
loop().
|
||||||
|
|
||||||
|
loop() ->
|
||||||
|
timer:sleep(300),
|
||||||
|
gameTick(),
|
||||||
|
loop().
|
||||||
|
|
||||||
|
gameTick() ->
|
||||||
|
GamePaused = gen_server:call(pong_bot, paused),
|
||||||
|
SendStart = gen_server:call(pong_bot, send_start),
|
||||||
|
|
||||||
|
case GamePaused of
|
||||||
|
true ->
|
||||||
|
case SendStart of
|
||||||
|
false ->
|
||||||
|
ggs_network:send_command("start"),
|
||||||
|
gen_server:cast(pong_bot, {send_start, true})
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
Ball = gen_server:call(pong_bot, ball),
|
||||||
|
{_, BallY} = Ball,
|
||||||
|
Me = gen_server:call(pong_bot, me),
|
||||||
|
{_, MeY} = Me,
|
||||||
|
|
||||||
|
case BallY < (MeY - 5) of
|
||||||
|
true ->
|
||||||
|
ggs_network:send_command("up");
|
||||||
|
false ->
|
||||||
|
ggs_network:send_command("down")
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ball(Pos_s) ->
|
||||||
|
PosList = string:tokens(Pos_s, ","),
|
||||||
|
XStr = lists:nth(1,PosList),
|
||||||
|
YStr = lists:nth(1,PosList),
|
||||||
|
X = string:to_integer(XStr),
|
||||||
|
Y = string:to_integer(YStr),
|
||||||
|
Pos = {X, Y},
|
||||||
|
gen_server:cast(pong_bot, {ball, Pos}).
|
||||||
|
|
||||||
|
player1_y(YStr) ->
|
||||||
|
Y = string:to_integer(YStr),
|
||||||
|
gen_server:cast(pong_bot, {player1_y, Y}).
|
||||||
|
|
||||||
|
player2_y(YStr) ->
|
||||||
|
Y = string:to_integer(YStr),
|
||||||
|
gen_server:cast(pong_bot, {player2_y, Y}).
|
||||||
|
|
||||||
|
game(WaitOrStart) ->
|
||||||
|
case WaitOrStart of
|
||||||
|
"wait" ->
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
gen_server:cast(pong_bot, {paused, false})
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
new_round() ->
|
||||||
|
Paused = true,
|
||||||
|
SendStart = false,
|
||||||
|
gen_server:cast(pong_bot, {new_round, Paused, SendStart}).
|
||||||
|
|
||||||
|
|
||||||
|
set_game_token(GameToken) ->
|
||||||
|
gen_server:cast({global, pong_bot}, {game_token, GameToken}).
|
||||||
|
|
||||||
|
get_game_token() ->
|
||||||
|
gen_server:call({global, pong_bot}, game_token).
|
||||||
|
|
||||||
|
handle_call(player1, _From, State) ->
|
||||||
|
Player1 = dict:fetch(player1, State),
|
||||||
|
{reply, Player1, State};
|
||||||
|
|
||||||
|
handle_call(player1_y, _From, State) ->
|
||||||
|
{_,Y} = dict:fetch(player1, State),
|
||||||
|
{reply, Y, State};
|
||||||
|
|
||||||
|
handle_call(player2_y, _From, State) ->
|
||||||
|
{_,Y} = dict:fetch(player2, State),
|
||||||
|
{reply, Y, State};
|
||||||
|
|
||||||
|
handle_call(game_token, _From, State) ->
|
||||||
|
io:format("Handle call game_token~n"),
|
||||||
|
GameToken = dict:fetch(game_token, State),
|
||||||
|
{reply, GameToken, State}.
|
||||||
|
|
||||||
|
handle_cast({me, Me}, State) ->
|
||||||
|
NewState = dict:store(me, Me, State),
|
||||||
|
{noreply, NewState};
|
||||||
|
|
||||||
|
handle_cast({ball, Pos}, State) ->
|
||||||
|
NewState = dict:store(ball, Pos, State),
|
||||||
|
{noreply, NewState};
|
||||||
|
|
||||||
|
handle_cast({player1_y, Y}, State) ->
|
||||||
|
{OldX, _} = dict:fetch(player1, State),
|
||||||
|
NewPlayer1 = {OldX, Y},
|
||||||
|
NewState = dict:store(player1, NewPlayer1, State),
|
||||||
|
{noreply, NewState};
|
||||||
|
|
||||||
|
handle_cast({player2_y, Y}, State) ->
|
||||||
|
{OldX, _} = dict:fetch(player2, State),
|
||||||
|
NewPlayer2 = {OldX, Y},
|
||||||
|
NewState = dict:store(player2, NewPlayer2, State),
|
||||||
|
{noreply, NewState};
|
||||||
|
|
||||||
|
handle_cast({paused, Paused}, State) ->
|
||||||
|
NewState = dict:store(paused, Paused, State),
|
||||||
|
{noreply, NewState};
|
||||||
|
|
||||||
|
handle_cast({new_rouned, Paused, SendStart}, State) ->
|
||||||
|
State1 = dict:store(paused, Paused, State),
|
||||||
|
NewState = dict:store(send_start, SendStart, State1),
|
||||||
|
{noreply, NewState};
|
||||||
|
|
||||||
|
handle_cast({game_token, Token}, State) ->
|
||||||
|
io:format("Handle cast game_token~n"),
|
||||||
|
NewState = dict:store(game_token, Token, State),
|
||||||
|
{noreply, NewState}.
|
Reference in a new issue