-module(test_port). -export([start/0, moving_body_init/3, schedule/1]). start() -> ExtProg = "python test_port.py", Port = open_port({spawn, ExtProg}, [stream]), Data = io_lib:format("~p: starting\n", [ self() ]), Port ! {self(), {command, Data}}, L = [ { start_pid(Port, { 100+X*70.0, 100+Y*70.0 }), 100+X*70.0, 100+Y*70.0 } || X <- lists:seq(1, 8), Y <- lists:seq(1, 4) ], %erlang:error_log(L), [ Pid ! { all_pids, L } || { Pid, _X, _Y } <- L], receive after 1000000 -> ok end, %Port ! {self(), close} ok . start_pid(Port, {X, Y}) -> spawn(test_port, moving_body_init, [Port, { X, Y }, 0.0 ]) . moving_body_init(Port, Position, Speed) -> {A1,A2,A3} = now(), random:seed(A1, A2, A3), Angle = random:uniform(360), % degrees spawn(test_port, schedule, [self()]), moving_body(Port, Position, Position, Speed, Angle, []) . schedule(Pid_to_be_scheduled) -> receive after 200 -> Pid_to_be_scheduled ! update_position end, schedule(Pid_to_be_scheduled) . moving_body(Port, Initial_position, Position, Speed, Angle, List_of_other_moving_bodies) -> receive { all_pids, L } -> % remove self() from the list { _, _, L2 } = lists:keytake(self(), 1, L), moving_body(Port, Initial_position, Position, Speed, Angle, L2) ; stop -> ok; { position, Pid, { X_pid, Y_pid } } -> % receive new position of another L = update_position(List_of_other_moving_bodies, Pid, X_pid, Y_pid), moving_body(Port, Initial_position, Position, Speed, Angle, L); update_position -> { X, Y } = Position, % current position New_angle = Angle + random:uniform(60) - 30, % compute new speed % distance from initial position {X0, Y0} = Initial_position, D2 = (X-X0)*(X-X0) + (Y-Y0)*(Y-Y0), New_speed_0 = (Speed + random:uniform(10) - 5) / (1+D2/10000), if New_speed_0 < 0 -> New_speed = 0; true -> New_speed = New_speed_0 end, Delta_x = math:cos(Angle * math:pi() / 180) * New_speed, Delta_y = math:sin(Angle * math:pi() / 180) * New_speed, X2 = X + Delta_x, Y2 = Y + Delta_y, % send new position to others [ Pid ! { position, self(), { X2, Y2 } } || { Pid, _Pid_x, _Pid_y } <- List_of_other_moving_bodies, Pid /= self() ], New_position = { X2, Y2 }, % compute the 3 nearest pids { Pid1, Pid2, Pid3 } = get_nearest_pids(New_position, List_of_other_moving_bodies), % send position to external process Data = io_lib:format("~p: x=~.2f, y=~.2f, pid1=~p, pid2=~p, pid3=~p\n", [ self(), X, Y, Pid1, Pid2, Pid3 ]), port_command(Port, Data), moving_body(Port, Initial_position, New_position, New_speed, New_angle, List_of_other_moving_bodies) end . get_nearest_pids(Pid_position, List_of_pids) -> { X0, Y0 } = Pid_position, % build list of all pids, and squares of distances L = [ { Pid, (X-X0)*(X-X0) + (Y-Y0)*(Y-Y0) } || { Pid, X, Y } <- List_of_pids ], L_sorted = lists:keysort(2, L), % get the 3 firts elements [ { Pid1, _ }, { Pid2, _ },{ Pid3, _ } | _Others ] = L_sorted, { Pid1, Pid2, Pid3 } . update_position([], _Pid, _X, _Y) -> [] ; update_position([ {Pid, _Old_x, _Old_y} | Others], Pid, X, Y) -> [ {Pid, X, Y} | Others ] ; update_position([ P_info | Others], Pid, X, Y) -> [ P_info | update_position(Others, Pid, X, Y) ] .