Skip to content

Instantly share code, notes, and snippets.

@7-fl
Created March 11, 2017 22:10
Show Gist options
  • Select an option

  • Save 7-fl/dc402b47e4f2092f6b713025cebdf5fa to your computer and use it in GitHub Desktop.

Select an option

Save 7-fl/dc402b47e4f2092f6b713025cebdf5fa to your computer and use it in GitHub Desktop.
rock/paper/scissors strategies
-module(rps).
-compile(export_all).
-include_lib("eunit/include/eunit.hrl").
%
% play one strategy against another, for N moves.
%
play_two(StrategyL,StrategyR,N) ->
play_two(StrategyL,StrategyR,[],[],N).
% tail recursive loop for play_two/3
% 0 case computes the result of the tournament
% FOR YOU TO DEFINE
% REPLACE THE dummy DEFINITIONS
play_two(_,_,PlaysL,PlaysR,0) ->
dummy;
play_two(StrategyL,StrategyR,PlaysL,PlaysR,N) ->
dummy.
%
% interactively play against a strategy, provided as argument.
%
play(Strategy) ->
io:format("Rock - paper - scissors~n"),
io:format("Play one of rock, paper, scissors, ...~n"),
io:format("... r, p, s, stop, followed by '.'~n"),
play(Strategy,[]).
% tail recursive loop for play/1
play(Strategy,Moves) ->
{ok,P} = io:read("Play: "),
Play = expand(P),
case Play of
stop ->
io:format("Stopped~n");
_ ->
Result = result(Play,Strategy(Moves)),
io:format("Result: ~p~n",[Result]),
play(Strategy,[Play|Moves])
end.
%
% auxiliary functions
%
% transform shorthand atoms to expanded form
expand(r) -> rock;
expand(p) -> paper;
expand(s) -> scissors;
expand(X) -> X.
% result of one set of plays
result(rock,rock) -> draw;
result(rock,paper) -> lose;
result(rock,scissors) -> win;
result(paper,rock) -> win;
result(paper,paper) -> draw;
result(paper,scissors) -> lose;
result(scissors,rock) -> lose;
result(scissors,paper) -> win;
result(scissors,scissors) -> draw.
% result of a tournament
tournament(PlaysL,PlaysR) ->
lists:sum(
lists:map(fun outcome/1,
lists:zipwith(fun result/2,PlaysL,PlaysR))).
outcome(win) -> 1;
outcome(lose) -> -1;
outcome(draw) -> 0.
% transform 0, 1, 2 to rock, paper, scissors and vice versa.
enum(0) ->
rock;
enum(1) ->
paper;
enum(2) ->
scissors.
val(rock) ->
0;
val(paper) ->
1;
val(scissors) ->
2.
% give the play which the argument beats.
beats(rock) ->
paper;
beats(paper) ->
scissors;
beats(scissors) ->
rock.
%
% strategies.
%
echo([]) ->
paper;
echo([Last|_]) ->
Last.
rock(_) ->
rock.
% FOR YOU TO DEFINE
% REPLACE THE dummy DEFINITIONS
%---------
no_repeat_test() ->
Play1 = no_repeat([]),
true = w2:member(Play1, plays()),
OpponentPlays = [rock],
Response = no_repeat(OpponentPlays),
[Hd|_] = OpponentPlays,
true = w2:member(Response, others(Hd)),
all_tests_passed.
no_repeat([]) -> %First play and no list of opponent's plays.
%After implementing rand(), I can call that instead:
%% Plays = plays(),
%% Rand = random:uniform(w2:len(Plays)),
%% get_elmt(Rand, Plays);
rand([]);
no_repeat([Play|_]) ->
Others = others(Play),
RandNum = random:uniform(w2:len(Others)),
get_elmt(RandNum, Others).
plays() ->
[rock, paper, scissors].
others_test() ->
[scissors, paper] = others(rock),
[scissors, rock] = others(paper),
[paper, rock] = others(scissors),
all_tests_passed.
others(Play) ->
others(Play, plays(), []).
others(_Play, [], Others) ->
Others;
others(Play, [Play|Ps], Others)->
others(Play, Ps, Others);
others(Play, [P|Ps], Others) ->
others(Play, Ps, [P|Others]).
%---------
%% Can't test this:
%%
%% const(Ps) ->
%% Plays = plays(),
%% Rand = random:uniform(lists:length(Plays)),
%% get_elmt(Rand, Plays).
rand_test() ->
Plays = plays(),
NumPlays = w2:len(Plays),
RandNum1 = random:uniform(NumPlays),
RandNum2 = random:uniform(NumPlays),
RandNum3 = random:uniform(NumPlays),
CorrectElmt1 = get_elmt(RandNum1, Plays),
CorrectElmt2 = get_elmt(RandNum2, Plays),
CorrectElmt3 = get_elmt(RandNum3, Plays),
RandFunc1 = fun(_X) -> RandNum1 end,
RandFunc2 = fun(_X) -> RandNum2 end,
RandFunc3 = fun(_X) -> RandNum3 end,
CorrectElmt1 = rand([], RandFunc1),
CorrectElmt2 = rand([rock], RandFunc2),
CorrectElmt3 = rand([rock,paper,scissors], RandFunc3),
all_tests_passed.
rand(Plays) ->
rand(Plays, fun random:uniform/1). %I implemented rand/2, so I could test rand/2.
rand(_Ps, RandFunc) ->
Plays = plays(),
RandNum = RandFunc(w2:len(Plays)),
get_elmt(RandNum, Plays).
get_elmt_test() ->
a = get_elmt(1, [a, b, c]),
b = get_elmt(2, [a, b, c]),
c = get_elmt(3, [a, b, c]),
all_tests_passed.
get_elmt(1, [P|_]) ->
P;
get_elmt(N, [_P|Ps]) when N>=1 ->
get_elmt(N-1, Ps).
%-----------------
cycle_test() ->
rock = cycle([]),
paper = cycle([rock]),
scissors = cycle([paper]),
rock = cycle([scissors]),
all_tests_passed.
cycle(Plays) -> %Algorithm: get play after opponent's play in plays() list.
cycle(Plays, plays()).
cycle([], [Response|_Plays]) -> %No opponent plays, response is first play.
Response;
cycle([P|_Ps], [P|[]]) -> %Opponent's play matches last one in plays(), response is first play.
[Response|_] = plays(),
Response;
cycle([P|_Ps], [P|Plays]) -> %Response is next play after opponent's play.
[Response|_] = Plays,
Response;
cycle(Ps, [_|Plays] ) ->
cycle(Ps, Plays).
%--------------
least_freq_test() ->
Response1 = beats(paper),
Response1 = least_freq([rock, scissors, paper, rock, scissors]),
Response2 = beats(rock),
Response2 = least_freq([rock, paper, paper, scissors, scissors]),
Response3 = least_freq([scissors]), %=> paper, rock will have freqs of 0
true = w2:member(Response3, [paper, scissors]), %beats [rock, paper] => [paper, scissors]
Response4 = least_freq([]),
true = w2:member(Response4, plays() ),
all_tests_passed.
least_freq([]) ->
rand([]);
least_freq(Plays) ->
Freqs = get_freqs(Plays),
MinFunc = fun(X, Y) -> %Compare the clarity of an if-stmt to
if %the nested case-stmt below.
X<Y -> repl;
X=:=Y -> add;
X>Y -> skip
end
end,
MinFreqs = freqs_by(Freqs, MinFunc), %There could be more than one...
RandNum = random:uniform( w2:len(MinFreqs) ), %so randomly pick one.
{Play, _Freq} = get_elmt(RandNum,MinFreqs), %Then return whatever Play beats it.
%io:format("~w~n", [Play]),
beats(Play).
%--------------
freqs_by_test() ->
%Collect minimum frequencies:
MinFunc = fun(X, Y) ->
case X<Y of
true -> repl;
false -> case X>Y of
true -> skip;
false -> add
end
end
end,
%Collect maximum frequencies:
MaxFunc = fun(X, Y) ->
case X<Y of
true -> skip;
false -> case X>Y of
true -> repl;
false -> add
end
end
end,
[{b,1}] = freqs_by([{a,2},{b,1},{c,3}], MinFunc),
[{c,1}, {b,1}] = freqs_by([{a,2},{b,1},{c,1}], MinFunc),
[{c,1}, {b,1}, {a,1}] = freqs_by([{a,1},{b,1},{c,1}], MinFunc),
[{c,3}] = freqs_by([{a,2},{b,1},{c,3}], MaxFunc),
[{c,2}, {a,2}] = freqs_by([{a,2},{b,1},{c,2}], MaxFunc),
[{c,1}, {b,1}, {a,1}] = freqs_by([{a,1},{b,1},{c,1}], MaxFunc),
all_tests_passed.
freqs_by([X|Xs], CompareF) ->
freqs_by(Xs, CompareF, [X]).
freqs_by([], _CompareF, Bests) ->
Bests;
freqs_by([ {_Name, Count}=X | Xs], CompareF, [ {_BestName, BestCount} | _]=Bests) ->
case CompareF(Count, BestCount) of
add -> freqs_by(Xs, CompareF, [X|Bests]);
repl -> freqs_by(Xs, CompareF, [X]);
skip -> freqs_by(Xs, CompareF, Bests)
end.
%-----------------
get_freqs_test() ->
[{scissors,0}, {paper, 0}, {rock, 0}] = get_freqs([]),
[{scissors,1}, {paper, 1}, {rock, 1}] = get_freqs([rock, paper, scissors]),
[{scissors,0}, {paper, 2}, {rock, 2}] = get_freqs([rock, paper, rock, paper]),
[{scissors,2}, {paper, 0}, {rock, 1}] = get_freqs([scissors, scissors, rock]),
[{scissors,0}, {paper, 2}, {rock, 1}] = get_freqs([paper, rock, paper]),
all_tests_passed.
get_freqs(OppPlays) ->
get_freqs(plays(), [], OppPlays).
get_freqs([], Counts, _) ->
Counts;
get_freqs([P|Ps], Counts, OppPlays) ->
Count = x:count(P, OppPlays),
get_freqs(Ps, [{P, Count} | Counts], OppPlays).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment