Colin MacDonald
%% beginning func(Input) -> Output = [], func(Input, Output).
%% beginning func(Input) -> Output = [], func(Input, Output). %% end func([], Output) -> lists:reverse(Output).
%% beginning func(Input) -> Output = [], func(Input, Output). %% end func([], Output) -> lists:reverse(Output). %% middle func([First | Rest], Output) -> NewFirst = munge(First), func(Rest, [NewFirst | Output]);
Foo = [cat, dog].
Foo | cat - dog
Foo = [cat, dog]. Bar = [monkey | Foo].
Bar Foo | | monkey - cat - dog
Foo = [cat, dog]. Bar = [monkey | Foo]. Baz = [elephant, tiger | Foo].
Baz Bar Foo | | | | monkey - cat - dog | / elephant - tiger /
%% Beginning: score/1 -> score/3 score(Rolls) -> Frame = 1, Score = 0, score(Rolls, Frame, Score).
%% Beginning score(Rolls) -> score(Rolls, 1, 0). %% End score(_Rolls, 11, Score) -> Score.
%% Beginning score(Rolls) -> score(Rolls, 1, 0). %% End score(_Rolls, 11, Score) -> Score; %% Middle score([Roll1, Roll2 | Rest], Frame, Score) -> NewScore = Score + Roll1 + Roll2, score(Rest, Frame + 1, NewScore).
%% Beginning score(Rolls) -> score(Rolls, 1, 0). %% End score(_Rolls, 11, Score) -> Score; %% Strike score([10 | Rest], Frame, Score) -> ... %% Spare score([Roll1, Roll2 | Rest], Frame, Score) when Roll1 + Roll2 == 10 -> ... %% Normal score([Roll1, Roll2 | Rest], Frame, Score) -> NewScore = Score + Roll1 + Roll2, score(Rest, Frame + 1, NewScore).
%% Beginning score(Rolls) -> score(Rolls, 1, 0). %% End score(_Rolls, 11, Score) -> Score; %% Strike score([10 | Rest], Frame, Score) -> [Bonus1, Bonus2 | _] = Rest, NewScore = Score + 10 + Bonus1 + Bonus2, score(Rest, Frame + 1, NewScore); %% Spare score([Roll1, Roll2 | Rest], Frame, Score) when Roll1 + Roll2 == 10 -> [Bonus1 | _] = Rest, NewScore = Score + 10 + Bonus1, score(Rest, Frame + 1, NewScore); %% Normal score([Roll1, Roll2 | Rest], Frame, Score) -> NewScore = Score + Roll1 + Roll2, score(Rest, Frame + 1, NewScore).
What about incomplete games?
score(Rolls) -> score(Rolls, 1, 0). score(_Rolls, 11, Score) -> Score; score([10 | Rest], Frame, Score) -> score(Rest, Frame + 1, Score + 10 + strike_bonus(Rest)); score([Roll1, Roll2 | Rest], Frame, Score) when (Roll1 + Roll2 == 10) -> score(Rest, Frame + 1, Score + 10 + spare_bonus(Rest)); score([Roll1, Roll2 | Rest], Frame, Score) -> score(Rest, Frame + 1, Score + Roll1 + Roll2); score([Roll1], _Frame, Score) -> Score + Roll1; score([], _Frame, Score) -> Score. spare_bonus([]) -> 0; spare_bonus([Bonus1 | _Rest]) -> Bonus1. strike_bonus([]) -> 0; strike_bonus([Only]) -> Only; strike_bonus([Bonus1, Bonus2 | _Rest]) -> Bonus1 + Bonus2.
Eshell V5.8.4 (abort with ^G) 1> Line = io:get_line("Next> "). Next>
Eshell V5.8.4 (abort with ^G) 1> Line = io:get_line("Next> "). Next> colin 4 "colin 4\n"
Eshell V5.8.4 (abort with ^G) 1> Line = io:get_line("Next> "). Next> colin 4 "colin 4\n" 2> [Player, RollText] = string:tokens(Line, " \t\n"). ["colin","4"]
Eshell V5.8.4 (abort with ^G) 1> Line = io:get_line("Next> "). Next> colin 4 "colin 4\n" 2> [Player, RollText] = string:tokens(Line, " \t\n"). ["colin","4"] 3> {Roll, _} = string:to_integer(RollText). {4,[]}
Eshell V5.8.4 (abort with ^G) 1> Line = io:get_line("Next> "). Next> colin 4 "colin 4\n" 2> [Player, RollText] = string:tokens(Line, " \t\n"). ["colin","4"] 3> {Roll, _} = string:to_integer(RollText). {4,[]} 4> GameData = dict:new(). {dict,0,...
Eshell V5.8.4 (abort with ^G) 1> Line = io:get_line("Next> "). Next> colin 4 "colin 4\n" 2> [Player, RollText] = string:tokens(Line, " \t\n"). ["colin","4"] 3> {Roll, _} = string:to_integer(RollText). {4,[]} 4> GameData = dict:new(). {dict,0,... 5> NewGameData = dict:append(Player, Roll, GameData). {dict,1,...
Eshell V5.8.4 (abort with ^G) 1> Line = io:get_line("Next> "). Next> colin 4 "colin 4\n" 2> [Player, RollText] = string:tokens(Line, " \t\n"). ["colin","4"] 3> {Roll, _} = string:to_integer(RollText). {4,[]} 4> GameData = dict:new(). {dict,0,... 5> NewGameData = dict:append(Player, Roll, GameData). {dict,1,... 6> {ok, Rolls} = dict:find(Player, NewGameData). {ok,[4]}
Eshell V5.8.4 (abort with ^G) 1> Line = io:get_line("Next> "). Next> colin 4 "colin 4\n" 2> [Player, RollText] = string:tokens(Line, " \t\n"). ["colin","4"] 3> {Roll, _} = string:to_integer(RollText). {4,[]} 4> GameData = dict:new(). {dict,0,... 5> NewGameData = dict:append(Player, Roll, GameData). {dict,1,... 6> {ok, Rolls} = dict:find(Player, NewGameData). {ok,[4]} 7> Score = bowling_game:score(Rolls). 4
loop(GameData) -> Line = io:get_line("Next> "), [Player, RollText] = string:tokens(Line, " \t\n"), {Roll, _} = string:to_integer(RollText), NewGameData = dict:append(Player, Roll, GameData), {ok, Rolls} = dict:find(Player, NewGameData). Score = bowling_game:score(Rolls). io:format("New score for ~s: ~p~n", [Player, Score]), loop(NewGameData).
main([]) -> loop(dict:new()). loop(GameData) -> ...
#!/usr/local/bin/escript # # scorekeeper.erl -import(bowling_game). main([]) -> loop(dict:new()). loop(GameData) -> ...
$ ./scorekeeper.erl Next>
$ ./scorekeeper.erl Next> colin 3 New score for colin: 3 Next>
$ ./scorekeeper.erl Next> colin 3 New score for colin: 3 Next> colin 4 New score for colin: 7 Next>
$ ./scorekeeper.erl Next> colin 3 New score for colin: 3 Next> colin 4 New score for colin: 7 Next> colin 10 New score for colin: 17 Next>
$ ./scorekeeper.erl Next> colin 3 New score for colin: 3 Next> colin 4 New score for colin: 7 Next> colin 10 New score for colin: 17 Next> colin 3 New score for colin: 23 Next>
Remember the command-line loop?
loop(GameData) -> Line = io:get_line("Next> "), [Player, RollText] = string:tokens(Line, " \t\n"), {Roll, _} = string:to_integer(RollText), NewGameData = dict:append(Player, Roll, GameData), {ok, Rolls} = dict:find(Player, NewGameData). Score = bowling_game:score(Rolls). io:format("New score for ~s: ~p~n", [Player, Score]), loop(NewGameData).
Here’s the message-handling loop.
loop(GameData) -> receive {From, {append, Player, RollText}} -> %% This is the same {Roll, _} = string:to_integer(RollText), NewGameData = dict:append(Player, Roll, GameData), {ok, Rolls} = dict:find(Player, NewGameData), Score = bowling_game:score(Rolls), From ! Score, loop(NewGameData) end.
loop(GameData) -> receive {From, {append, Player, RollText}} -> {Roll, _} = string:to_integer(RollText), NewGameData = dict:append(Player, Roll, GameData), {ok, Rolls} = dict:find(Player, NewGameData), Score = bowling_game:score(Rolls), From ! Score, loop(NewGameData) end. init() -> Data = dict:new(), Start = fun() -> loop(Data) end, spawn(Start). % returns pid
loop(GameData) -> receive {From, {append, Player, RollText}} -> {Roll, _} = string:to_integer(RollText), NewGameData = dict:append(Player, Roll, GameData), {ok, Rolls} = dict:find(Player, NewGameData), Score = bowling_game:score(Rolls), From ! Score, loop(NewGameData) end. init() -> Data = dict:new(), Start = fun() -> loop(Data) end, spawn(Start). % returns pid append(Player, RollText, Pid) -> Pid ! {self(), {append, Player, RollText}}, receive Resp -> Resp end.
http://localhost:8000/add/Player/Roll
e.g.
http://localhost:8000/add/colin/4
-module(bowling_web). -behaviour(spooky). -export([init/1, get/2]). % Spooky API
-module(bowling_web). -behaviour(spooky). -export([init/1, get/2]). % Spooky API -import(bowling_service). init([])-> Pid = bowling_service:init(), register(bowl_svc, Pid), [{port, 8000}].
-module(bowling_web). -behaviour(spooky). -export([init/1, get/2]). % Spooky API -import(bowling_service). init([])-> register(bowl_svc, bowling_service:init()), [{port, 8000}]. %% REST handler get(_Req, ["add", Player, RollText])-> ...
-module(bowling_web). -behaviour(spooky). -export([init/1, get/2]). % Spooky API -import(bowling_service). init([])-> register(bowl_svc, bowling_service:init()), [{port, 8000}]. %% REST handler get(_Req, ["add", Player, RollText])-> Score = bowling_service:append(Player, RollText, bowl_svc), {200, io_lib:format("~p", [Score])};
-module(bowling_web). -behaviour(spooky). -export([init/1, get/2]). % Spooky API -import(bowling_service). init([])-> register(bowl_svc, bowling_service:init()), [{port, 8000}]. %% REST handler get(_Req, ["add", Player, RollText])-> Score = bowling_service:append(Player, RollText, bowl_svc), {200, io_lib:format("~p", [Score])}; %% static page handlers get(Req, [])-> get(Req, ["form.html"]); % main page get(_Req, Path)-> % other static resources Filename = filename:join(Path), case file:read_file(Filename) of {ok, PageBytes} -> {200, binary_to_list(PageBytes)}; {error, Reason} -> {404, Reason} end.
$ erl -pa $SPOOKY/ebin -pa $SPOOKY/deps/*/ebin ... Eshell V5.8.4 (abort with ^G) 1> spooky:start_link(bowling_web). {ok,<0.35.0>} 2>
#!/usr/local/bin/escript main([]) -> SpookyDir = os:getenv("SPOOKY_DIR"), %% Add spooky and its dependencies to the code path. true = code:add_path(SpookyDir ++ "/ebin"), Deps = filelib:wildcard(SpookyDir ++ "/deps/*/ebin"), ok = code:add_paths(Deps), %% Compile our modules, just to be safe. c:c(bowling_game), c:c(bowling_service), c:c(bowling_web), spooky:start_link(bowling_web), io:format("Started spooky~n"), io:get_line("Return to exit... "), spooky:stop().