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).

浙公网安备 33010602011771号