From 9986a801aa8785a2c0846a32096de1515af8e809 Mon Sep 17 00:00:00 2001 From: Kallfaktorn Date: Sat, 16 Apr 2011 19:40:07 +0200 Subject: [PATCH] Pong bot for erlang. --- games/pong_bot_e/ggs_network.erl | 123 +++++++++++++++++++++ games/pong_bot_e/pong_bot.erl | 182 +++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 games/pong_bot_e/ggs_network.erl create mode 100644 games/pong_bot_e/pong_bot.erl diff --git a/games/pong_bot_e/ggs_network.erl b/games/pong_bot_e/ggs_network.erl new file mode 100644 index 0000000..b590236 --- /dev/null +++ b/games/pong_bot_e/ggs_network.erl @@ -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). + + diff --git a/games/pong_bot_e/pong_bot.erl b/games/pong_bot_e/pong_bot.erl new file mode 100644 index 0000000..c75ebc0 --- /dev/null +++ b/games/pong_bot_e/pong_bot.erl @@ -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}.