All the previous versions of the game server share a lot of logic. It is good engineering practice to recognize repeated patterns and try to find generic solutions that can be used to construct specific solutions.
The essence of all the previous game servers can be summarized by the following function.
gen_game_loop(State, Callback) ->
receive
{request, Request, From} ->
{reply, Response, NewState} = Callback(Request, From, State),
From ! {reply, Response},
gen_game_loop(NewState, Callback)
end.
The function gen_game_loop/2
takes the following two arguments:
State
- the state of the server.Callback
function of arity 3. Erlang is a functional language and we can pass
functions as arguments to functions.This server is abstract and the gen_game_loop/2
function only implements the
following generic logic of a game server loop:
request
.Response
.NewState
.Response
as a reply
back to the requesting client.State
with NewState
.The following callback function is all that is needed to re-implement version 4 of the game servers together with generic game server.
callback_v4(version, _From, {Version, _, _} = State) ->
{reply, {version, Version}, State};
callback_v4(stats, _From, {_, _, Stats} = State) ->
{reply, {stats, Stats}, State};
callback_v4({guess, Guess}, From, {Version, Secret, Stats}) ->
Reply = case compare(Guess, Secret) of
eq -> {right, Guess};
lt -> {wrong, Guess, lt};
gt -> {wrong, Guess, gt}
end,
Event = element(1, Reply),
NewState = {Version, Secret, update_stats_v4(From, Event, Stats)},
{reply, Reply, NewState}.
To start a generic server we must supply a callback function.
start(gen, Secret) ->
spawn(?MODULE, gen_game_loop,
[{gen, Secret, init_stats_v4()}, fun callback_v4/3]).
Try to understand how callback_v4/3
works together with gen_game_loop/2
.
Compile the updated module from the Erlang shell.
20> c(game).
{ok, game}
21>
You will still get a few warnings.
Let’s try the new version of the server.
22>G = game:test(gen, 1, 20, 5).
<0.352.0>
P5 [1 , 20] X > 7
P1 [1 , 20] X < 20
P2 [1 , 20] X > 1
P1 [1 , 19] X < 16
P3 [1 , 20] X > 3
P4 [1 , 20] X < 19
P3 [4 , 20] X > 11
P5 [8 , 20] X > 8
P4 [1 , 18] X > 11
P4 [12, 18] X =:= 12 (3 guesses, PID = <0.356.0>)
P2 [2 , 20] X < 18
P1 [1 , 15] X > 11
P3 [12, 20] X < 13
P5 [9 , 20] X < 13
P2 [2 , 17] X > 10
P1 [12, 15] X < 15
P5 [9 , 12] X =:= 12 (4 guesses, PID = <0.357.0>)
P2 [11, 17] X =:= 12 (4 guesses, PID = <0.354.0>)
P3 [12, 12] X =:= 12 (4 guesses, PID = <0.355.0>)
P1 [12, 14] X < 13
P1 [12, 12] X =:= 12 (6 guesses, PID = <0.353.0>)
23>
Use the stats/1
function to view the game statistics.
23> game:stats(G).
{stats,[{<0.357.0>,{1,3}},
{<0.353.0>,{1,5}},
{<0.354.0>,{1,3}},
{<0.355.0>,{1,3}},
{<0.356.0>,{1,2}}]}
24>