Erlang实现的模拟kaboose(山寨kahoot)

房主可以创建房间,添加问题,激活答题,获取答题情况。。

玩家可以答题。

各个进程之间瞎78传系列:)

(以Kahoot为模板实现的问答toy code)

-module(kaboose).
-export([
	start/0,
	get_a_room/1,
	add_question/2,
	get_questions/1,
	play/1,
	next/1,
	timesup/1,
	join/2,
	leave/2,
	rejoin/2,
	guess/3,
	playerloop/2,
	add_toDist/2,
	correct_answer/1
	]).

request_reply(Pid,Request) ->
	Pid ! {self(), Request},
	receive
		{Pid, Reponse} -> Reponse
	end.
async(Pid, Message) ->
    Pid ! Message.

start() -> 
	try 
		Server = spawn(fun() -> serverloop([]) end),
		{ok, Server}
	catch 
		_ : _ -> {error,"Failed to crate a server!/n"}
	end.

%for server loop
get_a_room(Server) ->
	request_reply(Server, get_a_room).

%for room loop
add_question(Room, {Description, Answers}) ->
	Question = {Description, Answers},
	request_reply(Room, {add_question,Question}).
get_questions(Room) ->
	request_reply(Room, get_questions).
play(Room) ->
	request_reply(Room, play).

%for activeroom loop
next(ActiveRoom) ->
	request_reply(ActiveRoom,next).
timesup(ActiveRoom) ->
	request_reply(ActiveRoom,timesup).

%for player process
join(ActiveRoom, NickStr) ->
	Nick = list_to_atom(NickStr),
	case request_reply(ActiveRoom,{join,NickStr}) of
		{ok,{NickStr,has_joined_in,ActiveRoom}} ->
			Ref = spawn(fun () -> ok,playerloop(Nick,[ActiveRoom]) end),
			register(Nick, Ref),
			Conductor = request_reply(ActiveRoom,get_conductor),
			Active = request_reply(ActiveRoom,get_pslist),
			% Conductor ! {Conductor, {player_joined, Nick, Active}},
			async(Conductor, {Conductor,{player_joined, NickStr, Active}}),
			{ok,Ref};
		{error,NickStr,is_taken} ->
			{error,NickStr,is_taken}
	end.	
leave(ActiveRoom, Ref) ->
	Nick = request_reply(Ref,getplayer),
	NickStr = atom_to_list(Nick),
	case request_reply(ActiveRoom,{leave,NickStr}) of
		{ok,{NickStr,has_left_from,ActiveRoom}} ->
			Conductor = request_reply(ActiveRoom,get_conductor),
			Active = request_reply(ActiveRoom,get_pslist),
			% Conductor ! {Conductor, {player_left, Nick, Active}},
			async(Conductor, {Conductor,{player_left, NickStr, Active}}),
			request_reply(Ref,leave);
		{error, {NickStr, is_not_in,From}} ->
			{error, NickStr, is_not_in,From}
	end.
rejoin(ActiveRoom, Ref) ->
	Nick = request_reply(Ref,getplayer),
	NickStr = atom_to_list(Nick),
	case request_reply(ActiveRoom,{join,NickStr}) of
		{ok,{NickStr,has_joined_in,ActiveRoom}} ->
			Conductor = request_reply(ActiveRoom,get_conductor),
			Active = request_reply(ActiveRoom,get_pslist),
			async(Conductor, {Conductor,{player_joined, NickStr, Active}}),
			request_reply(Ref,{rejoin,ActiveRoom});
		{error,NickStr,is_taken} ->
			{error,NickStr,is_taken}
	end.
guess(ActiveRoom, Ref, Index) ->
	ActiveQuestions = request_reply(ActiveRoom,get_activequestion),
	Nick = request_reply(Ref,getplayer),
	NickStr = atom_to_list(Nick),
	ACRoom = request_reply(Ref,getActiveRoom),
	case ActiveQuestions of
		[] -> ok;
		_ -> case ACRoom of
				[ActiveRoom] -> request_reply(ActiveRoom,{guess,NickStr,Index});
				_ -> {error, NickStr, is_not_in, ActiveRoom}
			 end
	end.

%loops:
serverloop(Roomlist) ->
  receive  	
  	% Returns {Server, Room} on success or {error, Reason} if some error occured.
    {From, get_a_room} ->
      try 
        Room = spawn(fun() -> roomloop([]) end),
		From ! {self(), {ok, Room}},
		serverloop([Room|Roomlist])
	  catch 
	    _:_ -> From ! {self(), {error,"Failed to crate a room!/n"}},
		serverloop(Roomlist)
	  end
  end.

roomloop(QuestionList) ->
	receive
		{From, {add_question, {Description, Answers} } } ->
		  	  case is_string(Description) 
		  	  	   andalso member(fun ({correct, _}) -> true; (_) -> false end, Answers) of
				true -> From ! {self(), ok},
				        roomloop(lists:append(QuestionList,[{Description,Answers}])) ;
				false -> From ! {self(), {error, invalid_question}}, 
				         roomloop(QuestionList)
			  end;
		{From, get_questions} ->
			From ! {self(), QuestionList},
			roomloop(QuestionList);
		{From, play} ->
			CRef = From,
			ActiveRoom = spawn(fun () -> ok,quizloop(CRef,QuestionList,[],[],[],#{},#{}) end),%From becomes a conductor
			From ! {self(), {ActiveRoom,CRef}},
			roomloop(QuestionList)
	end.
quizloop(Conductor, QuestionList, ActiveQuestions, Players, Dist, LastQ, Total) ->
	% io:format("~n~p~p~p~p~p~p~p~n", 
 %  		[Conductor, Players, QuestionList, ActiveQuestions, Dist, LastQ, Total]),
	receive
		{From, get_activequestion} ->
			From ! {self(),ActiveQuestions},
			quizloop(Conductor,QuestionList,ActiveQuestions,Players, Dist, LastQ, Total);
		{From, get_conductor} ->
			From ! {self(),Conductor},
			quizloop(Conductor,QuestionList,ActiveQuestions,Players, Dist, LastQ, Total);
		{From, get_pslist} ->
			From ! {self(),length(Players)},
			quizloop(Conductor,QuestionList,ActiveQuestions,Players, Dist, LastQ, Total);
		
		{From, next} ->
			case From of
                Conductor ->
                    case ActiveQuestions of
                    	[] ->
		                    case QuestionList of
		                    	[] ->
				            		From ! {self(),  {error, no_left_question}},
				            		quizloop(Conductor,QuestionList,[],Players, Dist, LastQ, Total);
		                    	_ ->
				                    [Question|Leftlist] = QuestionList,
				                    {Description,Answers} = Question,
				                    From ! {self(), {ok, {Description, Answers}}},
				                    Length = length(Answers),
				                    NewDist = lists:duplicate(Length, 0),
				                    quizloop(Conductor,Leftlist,[Question],Players, NewDist, LastQ, Total) 
				                    %change the Dist to list of 0 with right length		            
					        end;
			            _ ->
			            		From ! {self(),  {error, has_active_question}},
                    			quizloop(Conductor,QuestionList,ActiveQuestions,Players, Dist, LastQ, Total)
                    end;
                _ ->
                    From ! {self(), {error, who_are_you,from_is,From,conductor_is,Conductor}},
                    quizloop(Conductor,QuestionList,ActiveQuestions,Players, Dist, LastQ, Total)
            end;
        {From, timesup} ->
        	case (ActiveQuestions =/= []) of
                    	true ->
			                    Final = QuestionList =:= [],
			                    From ! {self(), {ok, Dist, LastQ, Total, Final}},
			                    quizloop(Conductor,QuestionList,[],Players, Dist, #{}, Total);
			            false ->
			            		From ! {self(),  {error, no_question_asked}},
                    			quizloop(Conductor,QuestionList,[],Players, Dist, LastQ, Total)
			end;
		{From, {join, Nick}} ->
        	case lists:member(Nick,Players) of
        		false ->
        			From ! {self(), {ok,{Nick,has_joined_in,self()}}},
        			quizloop(Conductor,QuestionList,ActiveQuestions,[Nick|Players], Dist, LastQ, Total);
        		true ->
        			From ! {self(), {error, Nick, is_taken}},
        			quizloop(Conductor,QuestionList,ActiveQuestions,Players, Dist, LastQ, Total)
        	end;
        {From, {leave, Nick}} ->
        	case lists:member(Nick,Players) of
        		true ->
        			From ! {self(), {ok,{Nick,has_left_from,self()}}},
        			quizloop(Conductor,QuestionList,ActiveQuestions,lists:delete(Nick,Players), Dist, LastQ, Total);
        		false ->
        			From ! {self(), {error, Nick, is_not_in,self()}},
        			quizloop(Conductor,QuestionList,ActiveQuestions,Players, Dist, LastQ, Total)
        	end;
        {From, {guess,Nick,Index}} ->
        	[ActiveQuestion|_] = ActiveQuestions,
        	{_,Answers} = ActiveQuestion,
        	NewDist = add_toDist(Index,Dist),
        	Correct = correct_answer(Answers),
        	case lists:member(Index,Correct) of
        		true -> 
        			case maps:is_key(Nick,Total) of
        				true ->
		        			OldScore = maps:get(Nick,Total),
		        			NewTotal = maps:put(Nick, OldScore+1000, Total),%should have different score standard!
		        			NewLastQ = maps:put(Nick,1000,LastQ),
		        			From ! {self(),ok},
		        			quizloop(Conductor,QuestionList,ActiveQuestions,Players, NewDist, NewLastQ, NewTotal);
		        		false ->
		        			NewTotal = maps:put(Nick, 1000, Total),%should have different score standard!
		        			NewLastQ = maps:put(Nick,1000,LastQ),
		        			From ! {self(),ok},
		        			quizloop(Conductor,QuestionList,ActiveQuestions,Players, NewDist, NewLastQ, NewTotal)
		        	end;
        		false ->
        			case maps:is_key(Nick,Total) of
        				true ->
		        			NewLastQ = maps:put(Nick,0,LastQ),
		        			From ! {self(),ok},
		        			quizloop(Conductor,QuestionList,ActiveQuestions,Players, NewDist, NewLastQ, Total);
		        		false ->
		        			NewTotal = maps:put(Nick, 0, Total),%should have different score standard!
		        			NewLastQ = maps:put(Nick,0,LastQ),
		        			From ! {self(),ok},
		        			quizloop(Conductor,QuestionList,ActiveQuestions,Players, NewDist, NewLastQ, NewTotal)
		        	end,
        			From ! {self(),ok},
        			quizloop(Conductor,QuestionList,ActiveQuestions,Players, NewDist, LastQ, Total)
        	end        	
	end.

playerloop(Nick,ActiveRoom) ->
	receive 
		{From,getplayer} ->
			From ! {self(), Nick},
			playerloop(Nick,ActiveRoom);
		{From,getActiveRoom} ->
			From ! {self(), ActiveRoom},
			playerloop(Nick,ActiveRoom);
		{From,{join,Quiz}} ->
			From ! {self(), ok},
			playerloop(Nick,[Quiz|ActiveRoom]);
		{From,leave} ->
			From ! {self(), ok},
			playerloop(Nick,[]);
		{From,{rejoin,Quiz}} ->
			From ! {self(), ok},
			playerloop(Nick,[Quiz|ActiveRoom]);
		{From,next} ->
			From ! {self(), {error,i_am_not_the_conductor}},
			playerloop(Nick,ActiveRoom)
	end.

% helper functions:
is_string([]) -> true;
is_string([X|Xs]) -> X >= 0 andalso X < 128 andalso is_string(Xs);
is_string(_) -> false.

add_toDist(N,Dist) -> lists:sublist(Dist,N-1) ++ [lists:nth(N,Dist)+1] ++ lists:nthtail(N,Dist).

position(_, []) -> 0; 
position(N1,[N1|_]) -> 1; 
position(N1,[_|Ns]) -> 1 + position(N1,Ns). 

member(_, []) -> false;
member(Pred, [E | List]) ->
    case Pred(E) of
        true ->
            true;
        false ->
            member(Pred, List)
    end.

correct_answer(Answers) -> 
	Correct = lists:filter(fun ({correct, _}) -> true; (_) -> false end, Answers),
	lists:map(fun(X) -> position(X, Answers) end,Correct).

  

猜你喜欢

转载自www.cnblogs.com/hanani/p/9981214.html