diff --git a/Actors/ambient.erl b/Actors/ambient.erl new file mode 100644 index 0000000..b7bf1bb --- /dev/null +++ b/Actors/ambient.erl @@ -0,0 +1,56 @@ +-module(ambient). +-export([main/1, ambient/1]). + + %%%%% + % Ambient actor, an omniscient actor that represents the real state of the world. In particular, + % the ambient knows for each cell/parking its state (free or occupied). + % @param Chessboard: dict that represents the state of the world. The key is a tuple {X,Y} that represents the position of the parking + ambient(Chessboard) -> + receive + {isFree, PID, X, Y, Ref} -> %Request a car sends to the ambient + %io:format("AMB: Request from ~p for parking (~p,~p)~n",[PID,X,Y]), %DEBUG + %io:format("AMB: Parking (~p,~p) is free: ~p~n", [X,Y, undefined =:= dict:fetch({X,Y}, Chessboard)]), %DEBUG + %io:format("AMB: Reply to ~p with Ref ~p~n", [PID, Ref]), %DEBUG + PID ! {status, Ref, undefined =:= dict:fetch({X,Y}, Chessboard)}, %Reply to the car + ambient(Chessboard); + {park, PID, X, Y, Ref} -> + %io:fwrite("AMB: Update Value for park ~p occupied by ~p~n", [{X,Y}, PID]), %Update parkings + render ! {parked, PID, X, Y, true}, %DEBUG + ambient(dict:store({X,Y}, PID, Chessboard)); + {leave, PID, Ref} -> + %io:fwrite("AMB: PID ~p exit from parking ~n", [PID]), %Update parkings + case searchKey(Chessboard, PID) of + {X, Y} -> + %io:fwrite("AMB: Update Value for leave ~p free~n", [{X,Y}]), %Update parkings + render ! {parked, PID, X, Y, false}, + ambient(dict:store({X,Y}, undefined, Chessboard)); + [] -> + %io:fwrite("AMB: PID: ~p not parked before ~n", [PID]), + ambient(Chessboard) + end; + _ -> io:fwrite("AMB: No Pattern Matching Found!\n") + end. + + %%%%%% + %@params Dict: dict to search + %@params Value: value used search the key + %@return Key: key of the dict that has the value passed as parameter + %@dev: this function can return a key {X,Y} if the value exist in the dict or [] if the value doesn't exist + searchKey(Dict, Value) -> + dict:fold(fun(K, V, Acc) -> + case V of + Value -> K; + _ -> Acc + end + end, [], Dict). + + %%%%% + % Main of ambient actor it spawns the ambient actor and registers it with the name ambient + % @return PID_A : PID of ambient actor + main(Chessboard) -> + PID_A = spawn(?MODULE, ambient, [Chessboard]), %spawn the ambient actor + register(ambient, PID_A), %register the ambient actor with the name ambient + io:format("AMBIENT: Correctly registered ~p as 'ambient' ~n", [PID_A]), %DEBUG + PID_A. + + \ No newline at end of file diff --git a/Actors/car.erl b/Actors/car.erl new file mode 100644 index 0000000..c3e6d09 --- /dev/null +++ b/Actors/car.erl @@ -0,0 +1,300 @@ +-module(car). +-export([main/2,friendship/2, friendship/3, state/4, state/7, detect/7, getFriends/4]). + + receive_friends(FriendsList, RefList, L, PID_S) -> + receive + {myFriends, PIDSLIST, Ref} -> + %Demonitor the old friends from refList + lists:foreach(fun(X) -> demonitor(X) end, RefList), + %Add the new friends to the list + TotalFriends = lists:usort(PIDSLIST ++ FriendsList), + %Create a list choosing 5 random friends from TotalFriends + FriendList2 = lists:sublist([Y||{_,Y} <- lists:sort([ {rand:uniform(), N} || N <- TotalFriends])], 5), + %Monitor all the friends in the list and save the ref in RefList + RefList2 = lists:map(fun({PIDF, _}) -> monitor(process, PIDF) end, FriendList2), + %io:format("FRI: I'm ~p my friends are:~p~n ", [self(), FriendList2]), + + PID_S ! {listModified, FriendList2}, + timer:sleep(5000), + case length(FriendList2) < 5 of + true -> getFriends(FriendList2, RefList2, L, PID_S); + false -> {FriendList2, RefList2} + end + end. + + + + getFriends(FriendsList, RefList, L, PID_S) -> + Lenght = length(L), + case Lenght of + 0 -> + Ref = make_ref(), + wellknown ! {getFriends, self(), PID_S, Ref}, + receive_friends(FriendsList, RefList, L, PID_S); + 5 -> + {FriendsList, RefList}; + _ -> + %pick a random friend from the list + {PIDF, _} = lists:nth(rand:uniform(Lenght), L), + %delete the friend from the list + io:format("FRI: I'm ~p I'm sending getFriends to ~p~n", [self(), PIDF]), + L2 = [ {PIDFR, PIDSTATE} || {PIDFR, PIDSTATE} <- L, PIDFR =/= PIDF], + Ref = make_ref(), + PIDF ! {getFriends, self(), PID_S, Ref}, + receive_friends(FriendsList, RefList, L2, PID_S) + end. + + friendship(FriendsList, RefList, PID_S) -> + %io:format("FRI: start friendship with PID: ~p~n", [self()]), + + case length(FriendsList) of + 5 -> + %io:format("FRI: I'm ~p I have 5 friends:~p~n ", [self(), FriendsList]), + receive + {getFriends, PIDF, PIDS, Ref} -> + %io:format("FRI: I'm ~p receiving get friends from ~p~n", [self(), PIDF]), + PIDF ! {myFriends, FriendsList, Ref}, + friendship(FriendsList, RefList, PID_S); + %case a friend dies + {'DOWN', _, _, PID, Reason } -> + io:format("FRI: Died PID: ~p, Reason: ~p~n", [PID, Reason]), + %delete the friend from the list + L2 = [ {PIDFR, PIDSTATE} || {PIDFR, PIDSTATE} <- FriendsList, PIDFR =/= PID], + friendship(L2, RefList, PID_S) + end; + _ -> + {FriendList2, RefList2} = getFriends(FriendsList, RefList, FriendsList, PID_S), + PID_S ! {listModified, FriendList2}, %Send to state the new list of friends + friendship(FriendList2, RefList2, PID_S) + + end. + + + %%%% + % @param: PID_S: PID of the state actor + % @dev: This function is used to link friendship actor to the state actor and to the detect actor. + % Now, F is linked to S and F is linked to D. Given that F and D are linked, S is linked to D. + % After that it calls friendship/3 that is the main function of the friendship actor + friendship(PID_S, PID_D) -> + link(PID_S), + link(PID_D), + friendship([], [], PID_S). + + + state(PID_D, World_Knowledge, X_Goal, Y_Goal, H, W, FriendList) -> + %io:format("Start State~n"), + receive + {updateState, PID_D, X, Y, IsFree} -> + %io:fwrite("Update Value for park (~p,~p), new value: ~p~n", [X,Y, IsFree]), %Update parkings + case {IsFree, dict:fetch({X,Y}, World_Knowledge)==IsFree} of + %If a park becames free, the state actor updates the knowledge of the world + %It doesn't update the goal coordinates because this case doesn't affect s + {_,true} -> state(PID_D, World_Knowledge, X_Goal, Y_Goal, H, W, FriendList); + {true, false}-> + World_Knowledge2 = dict:store({X,Y}, IsFree, World_Knowledge), + lists:foreach(fun({_, PIDSTATE}) -> PIDSTATE ! {notifyStatus, X, Y, IsFree} end, FriendList), + state(PID_D, World_Knowledge2, X_Goal, Y_Goal, H, W, FriendList); + %If a park becames busy I have to check if it was the goal + {false,false} -> + World_Knowledge2 = dict:store({X,Y}, IsFree, World_Knowledge), + lists:foreach(fun({_, PIDSTATE}) -> PIDSTATE ! {notifyStatus, X, Y, IsFree} end, FriendList), + case {X=:=X_Goal, Y =:= Y_Goal} of + %If the goal is busy, I have to generate a new goal + {true,true} -> + io:fwrite("DISCOVER I HAVE TO CREATE A NEW GOAL: ~p~n", [{X,Y}]), + {X_Goal_New, Y_Goal_New} = generate_coordinates(H, W), + PID_D ! {updateGoal, X_Goal_New, Y_Goal_New}, + state(PID_D, World_Knowledge2, X_Goal_New, Y_Goal_New, H, W,FriendList); + %Else update the knowledge of the world + {_,_} -> + state(PID_D, World_Knowledge2, X_Goal, Y_Goal, H, W,FriendList) + end + end; + + %Case Car Exit from the parking and needs new goal + {askNewGoal, PID_D, Ref} -> + io:format("Ask New Goal with Ref ~p~n",[Ref]), + {X_Goal_New, Y_Goal_New} = generate_coordinates(H, W), %TODO: check if new coordinates I generate should be free? + PID_D ! {responseNewGoal, X_Goal_New, Y_Goal_New, Ref}, + state(PID_D, World_Knowledge, X_Goal_New, Y_Goal_New, H, W,FriendList); + + {notifyStatus, X,Y, IsFree} -> + %io:format("CAR: Notify Status {~p,~p} : ~p ~n", [X,Y,IsFree]), + case dict:fetch({X,Y}, World_Knowledge)==IsFree of + true -> + %Do nothing + state(PID_D, World_Knowledge, X_Goal, Y_Goal, H, W,FriendList); + false -> + %World changed, notify to friends + World_Knowledge2 = dict:store({X,Y}, IsFree, World_Knowledge), + lists:foreach(fun({_, PIDSTATE}) -> PIDSTATE ! {notifyStatus, X, Y, IsFree} end, FriendList), + case {X=:=X_Goal, Y =:= Y_Goal} of + %If the goal is busy, I have to generate a new goal + {true,true} -> + %io:fwrite("NOTIFIED I HAVE TO CREATE A NEW GOAL: ~p~n", [{X,Y}]), + {X_Goal_New, Y_Goal_New} = generate_coordinates(H, W), + PID_D ! {updateGoal, X_Goal_New, Y_Goal_New}, + state(PID_D, World_Knowledge2, X_Goal_New, Y_Goal_New, H, W,FriendList); + %Else update the knowledge of the world + {_,_} -> + state(PID_D, World_Knowledge2, X_Goal, Y_Goal, H, W,FriendList) + end + end; + + %TODO: case a friend sends me a new list of friends + {listModified, NewFriendList} -> + %io:format("Received new list of friends: ~p~n", [NewFriendList]), + + render ! {friends, PID_D, NewFriendList}, %send to render the new list of friends + state(PID_D, World_Knowledge, X_Goal, Y_Goal, H, W , NewFriendList); + + %Default + _ -> state(PID_D, World_Knowledge, X_Goal, Y_Goal, H, W,FriendList) + end. + + + state(X_Goal, Y_Goal, H, W) -> + World_Knowledge = dict:from_list([{{X, Y}, undefined} || X <- lists:seq(0, H-1), Y <- lists:seq(0, W-1)]), + receive + {pidDetect, PID_D} -> + state(PID_D, World_Knowledge, X_Goal, Y_Goal, H, W, []) + end. + + + generate_coordinates(H, W) -> + X = rand:uniform(H)-1, + Y = rand:uniform(W)-1, + {X, Y}. + +%L'attore "detect" di un'automobile sceglie un posteggio obiettivo libero interagendo con l'attore "state". Dopodichè, ogni 2s, si avvicina di una cella verso tale obiettivo. Se deve muoversi lungo entrambi gli assi (x e y), lo fa scegliendo randomicamente l'asse e muovendosi nella direzione che minimizza la distanza percorsa. + + %%%% + % @param X: actual X coordinate of the car + % @param X_Goal: X coordinate of the goal + % @param H: height of the chessboard + % @return: 1 or -1 + % @dev: This function computes the movement along the X axis that minimizes the distance to the goal. + % If the distance is the same, the function returns 1. Since pacman effect is allowed (the car can move from the last cell to the + % first one and viceversa), the function must consider the modulo operation. + compute_X_movement(X, X_Goal, H) -> + D_pos = abs(X_Goal - ((X+1) rem H)), + D_neg = abs(X_Goal - ((X-1) rem H)), + %io:format("X: D_pos: ~p, D_neg: ~p~n", [D_pos, D_neg]), + case D_pos =< D_neg of + true -> 1; + false -> -1 + end. + + %%%% + % @param Y: actual Y coordinate of the car + % @param Y_Goal: Y coordinate of the goal + % @param W: width of the chessboard + % @return: 1 or -1 + % @dev: This function computes the movement along the Y axis that minimizes the distance to the goal. + % If the distance is the same, the function returns 1. Since pacman effect is allowed (the car can move from the last cell to the + % first one and viceversa), the function must consider the modulo operation. + compute_Y_movement(Y, Y_Goal, W) -> + D_pos = abs(Y_Goal - ((Y+1) rem W)), + D_neg = abs(Y_Goal - ((Y-1) rem W)), + %io:format("Y: D_pos: ~p, D_neg: ~p~n", [D_pos, D_neg]), + case D_pos =< D_neg of + true -> 1; + false -> -1 + end. + + %%%% + % @param X: actual X coordinate of the car + % @param Y: actual Y coordinate of the car + % @param X_Goal: X coordinate of the goal + % @param Y_Goal: Y coordinate of the goal + % @param H: height of the chessboard + % @param W: width of the chessboard + % @return: {X,Y} coordinates of the next cell + % @dev: This function computes the next cell to reach the goal. + % If the car is already in the goal this function sends the message "park" to the ambient actor. + % If the car is not in the goal, the function computes the next cell to reach the goal. + % If the car is not in the goal and it must move along both the axes, the function chooses randomly the axis and the direction. + move(X, Y, {X_Goal, Y_Goal}, H, W) -> + + case {X =:= X_Goal, Y =:= Y_Goal} of + {true, true} -> + {X, Y}; + {true, false}-> + {X, (Y + compute_Y_movement(Y, Y_Goal, W)) rem (W)}; + {false, true} -> + {(X + compute_X_movement(X, X_Goal, H)) rem (H), Y}; + {false, false} -> + case random:uniform(2) of + 1 -> {(X + compute_X_movement(X, X_Goal, H+1)) rem (H), Y}; + 2 -> {X, (Y + compute_Y_movement(Y, Y_Goal, W+1)) rem (W)} + end + end. + %%%%%% + %@param X: actual X coordinate of the car + %@param Y: actual Y coordinate of the car + % + detect(X, Y, X_Goal, Y_Goal, H, W, PID_S) -> + %io:format("Start Detect with goal (~p,~p)~n", [X_Goal, Y_Goal]), + timer:sleep(2000), + {X_New, Y_New} = move(X, Y, {X_Goal, Y_Goal}, H, W), %TODO: H and W must be passed as parameters? + render ! {position, self(), X_New, Y_New}, + timer:sleep(5000), %TODO: just for debug() + Ref = make_ref(), + %io:format("Ref ~p~n", [Ref]), + ambient ! {isFree, self(), X_New, Y_New, Ref}, + %io:format("I'm here ~p~n",[self()]), + receive + {updateGoal, X_Goal_New, Y_Goal_New} -> detect(X_New, Y_New, X_Goal_New, Y_Goal_New, H, W, PID_S); + {status, Ref, IsFree} -> + %io:format("Received status ~p with Ref ~p~n", [IsFree, Ref]), + PID_S ! {updateState, self(), X_New, Y_New, IsFree}, + case {X_New =:= X_Goal, Y_New =:= Y_Goal} of + {true, true} -> + Park_Ref = make_ref(), + ambient ! {park, self(), X_New, Y_New, Park_Ref}, + timer:sleep(rand:uniform(5)*1000), + ambient ! {leave, self(), Park_Ref}, + PID_S ! {askNewGoal, self(), Park_Ref}, + receive + {responseNewGoal, X_Goal_New, Y_Goal_New, Park_Ref} -> + io:format("Received new goal (~p, ~p) with Ref: ~p~n", [X_Goal_New, Y_Goal_New, Park_Ref]), + render ! {target, self(), X_Goal_New, Y_Goal_New}, + detect(X_New, Y_New, X_Goal_New, Y_Goal_New, H, W, PID_S); + Msg -> io:fwrite("MSG: ~p~n",[Msg]) %TODO: Kill process? + end; + _ -> detect(X_New, Y_New, X_Goal, Y_Goal, H, W, PID_S) + + end; + X -> io:fwrite("~p~n",[X]) %TODO: Kill process? + end. + + %The main actor creates other actors and re-creates them if they fail + main(H, W) -> + process_flag(trap_exit, true), + %io:format("X_Spawn: ~p, Y_Spawn: ~p~n", [X_Spawn, Y_Spawn]), + %io:format("X_Goal: ~p, Y_Goal: ~p~n", [X_Goal, Y_Goal]), + + Spawn_loop = fun Spawn_loop() -> + + {X_Spawn, Y_Spawn} = generate_coordinates(H, W), + {X_Goal, Y_Goal} = generate_coordinates(H, W), + PID_S = spawn(?MODULE, state, [X_Goal, Y_Goal, H, W]), + PID_D = spawn_link(?MODULE, detect, [X_Spawn, Y_Spawn, X_Goal, Y_Goal, H, W, PID_S]), + PID_S ! {pidDetect, PID_D}, + PID_F = spawn(?MODULE, friendship, [PID_S, PID_D]), + render ! {target, PID_D, X_Goal, Y_Goal}, + render ! {position, PID_D, X_Spawn, Y_Spawn}, + receive + {'EXIT', PID, Reason } -> + io:format("Died PID: ~p, Reason: ~p~n", [PID, Reason]), + Spawn_loop() + end + end, + Spawn_loop(). + + + + + + + diff --git a/Actors/main.erl b/Actors/main.erl new file mode 100644 index 0000000..0055f19 --- /dev/null +++ b/Actors/main.erl @@ -0,0 +1,54 @@ +-module(main). +-export([main/0, kill_spawn_loop/3, spawn_loop/4, kill_loop/2]). + +kill_spawn_loop(CarList, H, W) -> + N_Cars = rand:uniform(4), + NewList = kill_loop(N_Cars, CarList), + NewList2 = spawn_loop(N_Cars, NewList, H, W), + timer:sleep(10000), + kill_spawn_loop(NewList2, H, W). + +spawn_loop(N_Cars_to_Spawn, CarList, H, W) -> + case N_Cars_to_Spawn > 0 of + true -> + {PID_M, Ref_monitor} = spawn_monitor(car, main, [H,W]), + io:format("MAIN: Spawned car with PID: ~p~n", [PID_M]), + CarList2 = lists:append(CarList, [PID_M]), + timer:sleep(5000), + render ! {print}, + spawn_loop(N_Cars_to_Spawn-1, CarList2, H, W); + false -> + CarList + end. + +kill_loop(N_Cars_to_Kill, CarList) -> + case N_Cars_to_Kill > 0 of + true -> + %Choose randomly a car to kill + PID_M2 = lists:nth(rand:uniform(length(CarList)), CarList), + exit(PID_M2, kill), + io:format("MAIN: Killed car with PID: ~p~n", [PID_M2]), + CarList2 = [ PIDM || PIDM <- CarList, PIDM =/= PID_M2], + kill_loop(N_Cars_to_Kill-1, CarList2); + false -> + CarList + end. + + + +main() -> + H = 10, + W = 10, + Chessboard = dict:from_list([{{X, Y}, undefined} || X <- lists:seq(0, H-1), Y <- lists:seq(0, W-1)]), + %TODO: MONITOR THE SPAWNED ACTORS ?? + PID_A = ambient:main(Chessboard), %spawn the ambient actor + PID_W = wellknown:main(), + PID_R = render:main([{X, Y} || X <- lists:seq(0, H-1), Y <- lists:seq(0, W-1)], dict:new()), + List = spawn_loop(10, [], H, W), + kill_spawn_loop(List, H, W). + + + + + + \ No newline at end of file diff --git a/Actors/render.erl b/Actors/render.erl new file mode 100644 index 0000000..ab7872e --- /dev/null +++ b/Actors/render.erl @@ -0,0 +1,99 @@ +%Render actors represent the ambient at the moment of the request. They are used to show the current state of the ambient to the user. +%Show in ASCII ART the ambient + +% Path: Actors\render.erl +-module(render). +-export([main/2, render/3]). + +print_chessboard([{X,Y}|T], DictToList) -> + case Y == 0 of + true -> io:format("\n"); + _ -> ok + end, + A = lists:keyfind({X,Y}, 2, DictToList), + B = lists:keyfind({X,Y}, 3, DictToList), + %The struct is {PID, {POS}, {GOAL}, [FRIENDS], ID} + case {A,B} of + {false, false} -> io:format("O\t"); + {{_,{X,Y},_,_, ID}, {_,_,{X,Y}, _, ID}} -> io:format("*~p*\t", [ID]); + {{_,{X,Y},_,_, ID}, _} -> io:format("~p\t", [ID]); + {_ , {_,_,{X,Y},_, ID}} -> io:format("~p*\t", [ID]) + end, + + %io:format("A IS EQUAL TO ~p~n", [Dict]), + print_chessboard(T, DictToList ); + print_chessboard([],_)-> io:format("\n"). + + +transform_print(Chessboard, NewDict) -> + DictToList = dict:to_list(NewDict), + DictToList2 = lists:map(fun({A, {B,C,D,E}}) -> io:format("R: Car ~p with PID ~p is Friend with ~p~n", [E,A,D]), {A,B,C,D,E} end, DictToList), + print_chessboard(Chessboard, DictToList2). + + +render(Chessboard, Dict, N) -> + % PID -> {{POS},{GOAL}, int} DICT STRUCTURE + receive + {print} -> + transform_print(Chessboard, Dict), + render(Chessboard, Dict, N); + % position of car sent by detect + {position, PID, X, Y} -> + case dict:find(PID, Dict) of + error -> + Dict2 = dict:store(PID, {{X,Y}, {undefined, undefined}, [], N}, Dict), + monitor(process, PID), + %transform_print(Chessboard, Dict2), + render(Chessboard, Dict2, N+1); + {ok, {{_,_},{X_Goal, Y_Goal}, FriendsList, ID}} -> + Dict2 = dict:store(PID, {{X,Y},{X_Goal, Y_Goal}, FriendsList, ID}, Dict), + %transform_print(Chessboard, Dict2), + render(Chessboard, Dict2, N) + end; + + % target position of goal sent by detect + {target, PID, X, Y} -> + case dict:find(PID, Dict) of + error -> + Dict2 = dict:store(PID, {{undefined, undefined},{X,Y}, [], N}, Dict), + monitor(process, PID), + %transform_print(Chessboard, Dict2), + render(Chessboard, Dict2, N+1); + {ok, {{X_Pos, Y_Pos}, {_,_}, FriendsList, ID}} -> + Dict2 = dict:store(PID, {{X_Pos, Y_Pos},{X,Y}, FriendsList, ID}, Dict), + %transform_print(Chessboard, Dict2), + render(Chessboard, Dict2, N) + end; + %sent by ambient when car park or restart + {parked, PID, X, Y, IsParked} -> + {_,_,_, Num} = dict:fetch(PID, Dict), + io:format("RENDER: Car N_~p with PID ~p is parked at (~p, ~p): ~p~n", [Num, PID, X, Y, IsParked]), + render(Chessboard, Dict, N); + %sent by friendship actor TODO: implement this + {friends, PID, PIDLIST} -> + case dict:find(PID, Dict) of + error -> + Dict2 = dict:store(PID, {{undefined, undefined}, {undefined, undefined}, PIDLIST, N}, Dict), + monitor(process, PID), + %transform_print(Chessboard, Dict2), + render(Chessboard, Dict2, N+1); + + {ok, {{X_Pos, Y_Pos}, {X_Goal, Y_Goal}, _, ID}} -> + Dict2 = dict:store(PID, {{X_Pos, Y_Pos},{X_Goal,Y_Goal}, PIDLIST ,ID}, Dict), + %transform_print(Chessboard, Dict2), + %io:format("RENDER: Car N_~p with PID: ~p is friend with ~p~n", [ID, PID, PIDLIST]), + render(Chessboard, Dict2, N) + end; + {'DOWN', _, _, PID, Reason } -> + {_,_,_, Num} = dict:fetch(PID, Dict), + io:format("RENDER: Died PID: ~p with N_~p, Reason: ~p~n", [PID, Num, Reason]), + %remove from dict + Dict2 = dict:erase(PID, Dict), + render(Chessboard, Dict2, N) + end. + +main(Chessboard, Dict) -> + PID_R = spawn(render, render, [Chessboard, Dict, 1]), + register(render, PID_R), + io:format("RENDER: Correctly registered ~p as 'render' ~n", [PID_R]), + PID_R. %DEBUG diff --git a/Actors/run.bat b/Actors/run.bat new file mode 100644 index 0000000..56eacd4 --- /dev/null +++ b/Actors/run.bat @@ -0,0 +1,6 @@ +#!/bin/bash +erlc -o ambient.erl +erlc -o render.erl +erlc -o car.erl + +erl -eval "ambient:main(3,3)" diff --git a/Actors/wellknown.erl b/Actors/wellknown.erl new file mode 100644 index 0000000..9654934 --- /dev/null +++ b/Actors/wellknown.erl @@ -0,0 +1,43 @@ +% +-module(wellknown). +-export([main/0, wellknown/1]). + +%%%%%%%% +%@param PIDSLIST: list of tuples {PIDF, PIDS} where PIDF is the PID of friendship actor and PIDS is the PID of state actor. +% Represents all the car that wellknown knows about. Initially it is empty +wellknown(PIDSLIST) -> + receive + %PID1 is the PID of friendship actor, PID2 is the PID of state actor + {getFriends, PID1, PID2, Ref} -> + %io:format("WK: getFriends received from PID: ~p~n", [PID1]), + %Create a new list that contains all elements form PIDSLIST except the one that makes the request cause no one is friend with himself + PIDSLIST2 = [ {PIDF, PIDS} || {PIDF, PIDS} <- PIDSLIST, PIDF =/= PID1], + PID1 ! {myFriends, PIDSLIST2, Ref}, + %io:format("WK: Sent myFriends to PID: ~p, myFriends:~p~n", [PID1, PIDSLIST]), + case lists:member({PID1, PID2}, PIDSLIST) of + %If the PID is already in my list + true -> wellknown(PIDSLIST); + false -> + %Monitor the friendship actor of the Car I added to my list so I can remove it if he dies + monitor(process, PID1), + %Add the new actor to the list + wellknown([{PID1, PID2}|PIDSLIST]) + end; + %Case a PID I monitor dies + {'DOWN', _, _, PID_Friendship, _Reason} -> + %io:format("WK: Delete PID: ~p for reason: ~p~n", [PID_Friendship, _Reason]), + %Create a new list that contains all elements form PIDSLIST except the one that died + UpdatedList = [ {PIDF, PIDS} || {PIDF, PIDS} <- PIDSLIST, PIDF =/= PID_Friendship], + wellknown(UpdatedList) + end. + + +%%%%% +% Main of wellknown actor it spawns the wellknown actor and registers it with the name wellknown +%@return PID_W : PID of wellknown actor +main() -> + PID_W = spawn(?MODULE, wellknown, [[]]), + register(wellknown, PID_W), + io:format("WN: Wellknown started with PID ~p registered with wellknown~n", [PID_W]), + PID_W. +