Created
May 26, 2020 20:34
-
-
Save dduugg/89457f3e7417fe3a895f96f21e9b29e3 to your computer and use it in GitHub Desktop.
Strategies exercises
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(rps). | |
-export([const/1, cycle/1, echo/1, enum/1, | |
hindsight_strat/1, least_freq/1, most_freq/1, | |
no_repeat/1, play/1, play_two/3, rand/1, rand_strat/1, | |
rock/1, test/0, tournament/2, val/1]). | |
% | |
% 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) -> | |
io:format("Result: ~p~n", [score(PlaysL, PlaysR)]); | |
play_two(StrategyL, StrategyR, PlaysL, PlaysR, N) -> | |
play_two(StrategyL, StrategyR, | |
[StrategyL(PlaysR) | PlaysL], | |
[StrategyR(PlaysL) | PlaysR], N - 1). | |
% helper function to provide aggregate score of two sequences of plays | |
score(PlaysL, PlaysR) -> score(PlaysL, PlaysR, 0). | |
score([], [], Score) -> Score; | |
score([PlayL | PlaysL], [PlayR | PlaysR], Score) -> | |
score(PlaysL, PlaysR, | |
outcome(result(PlayL, PlayR)) + Score). | |
score_test() -> | |
1 = score([paper, paper, paper, paper], | |
[rock, paper, scissors, rock]), | |
ok. | |
% | |
% 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) -> scissors; | |
beats(paper) -> rock; | |
beats(scissors) -> paper. | |
% give the play which beats the argument. | |
% this is used in frequency-based strategies. | |
beat_by(X) -> beats(beats(X)). | |
% | |
% strategies. | |
% | |
echo([]) -> paper; | |
echo([Last | _]) -> Last. | |
rock(_) -> rock. | |
% FOR YOU TO DEFINE | |
% REPLACE THE dummy DEFINITIONS | |
no_repeat([]) -> paper; | |
no_repeat([X | _]) -> | |
case X of | |
rock -> scissors; | |
paper -> rock; | |
scissors -> paper | |
end. | |
% ¯\_(ツ)_/¯ (no spec given) | |
const(Play) -> Play. | |
% A strategy which cycles through moves in the order of R, P, S | |
cycle(Xs) -> enum(length(Xs) rem 3). | |
% A strategy which chooses a move at random | |
rand(_) -> enum(rand:uniform(3) - 1). | |
% A strategy that expects to see the move used least by the opponent thus far. | |
least_freq(Xs) -> | |
{R, P, S} = tally(Xs), | |
Least = case {R, P, S} of | |
_ when R =< P andalso R =< S -> rock; | |
_ when P =< R andalso P =< S -> paper; | |
_ -> scissors | |
end, | |
beat_by(Least). | |
least_freq_test() -> | |
paper = least_freq([paper, scissors]), ok. | |
% A strategy that expects to see the move used most by the opponent thus far. | |
most_freq(Xs) -> | |
{R, P, S} = tally(Xs), | |
Most = case {R, P, S} of | |
_ when R >= P andalso R >= S -> rock; | |
_ when P >= R andalso P >= S -> paper; | |
_ -> scissors | |
end, | |
beat_by(Most). | |
most_freq_test() -> | |
scissors = most_freq([paper, scissors, paper, rock]), | |
ok. | |
% helper function to produce a tally of RPS choices from a list of moves | |
tally(Xs) -> tally(Xs, {0, 0, 0}). | |
tally_test() -> | |
{3, 0, 1} = tally([rock, rock, rock, scissors]), ok. | |
tally([], RPS) -> RPS; | |
tally([X | Xs], {R, P, S}) -> | |
RPS = case X of | |
rock -> {R + 1, P, S}; | |
paper -> {R, P + 1, S}; | |
scissors -> {R, P, S + 1} | |
end, | |
tally(Xs, RPS). | |
% A stategy that chooses randomly from a list of strategies | |
rand_strat(Strats) -> | |
fun (Xs) -> | |
(lists:nth(rand:uniform(length(Strats)), Strats))(Xs) | |
end. | |
% A strategy which chooses the best-performing strategy from | |
% a list, if used in hindsight | |
hindsight_strat(Strats) -> | |
fun (Xs) -> | |
StratScores = lists:zip(Strats, | |
hindsight_scores(Xs, Strats)), | |
{Strat, _} = best_strat(StratScores), | |
Strat(Xs) | |
end. | |
hindsight_strat_test() -> | |
Strat = hindsight_strat([fun rock/1, fun cycle/1]), | |
% this is rock when rock/1 and paper when cycle/1: | |
rock = Strat([scissors, scissors, scissors]), | |
ok. | |
% returns the best Strategy & Score tuple from a list | |
best_strat([{Strat, Score}]) -> {Strat, Score}; | |
best_strat([{Strat, Score} | Xs]) -> | |
{Strat2, Score2} = best_strat(Xs), | |
case Score > Score2 of | |
true -> {Strat, Score}; | |
false -> {Strat2, Score2} | |
end. | |
% helper function which returns a list of scores corresponding | |
% to applying each of a list of strategies against a sequence of moves | |
hindsight_scores(_, []) -> []; | |
hindsight_scores(Xs, [Strat | Strats]) -> | |
[hindsight_score(Xs, Strat) | hindsight_scores(Xs, | |
Strats)]. | |
hindsight_scores_test() -> | |
[0, 2] = hindsight_scores([rock, paper, scissors, | |
scissors, scissors], | |
[fun cycle/1, fun rock/1]), | |
ok. | |
% returns the score of a strategy used for a list of Moves | |
hindsight_score(Xs, Strat) -> | |
hindsight_score(Xs, Strat, 0). | |
hindsight_score([], _, Outcome) -> Outcome; | |
hindsight_score([X | Xs], Strat, Outcome) -> | |
hindsight_score(Xs, Strat, | |
Outcome + outcome(result(Strat(Xs), X))). | |
hindsight_score_test() -> | |
1 = hindsight_score([rock, paper, scissors, scissors], | |
fun rock/1). | |
test() -> | |
score_test(), | |
least_freq_test(), | |
most_freq_test(), | |
tally_test(), | |
hindsight_strat_test(), | |
hindsight_scores_test(), | |
hindsight_score_test(), | |
ok. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@elbrujohalcon that's a great suggestion, thanks for the thoughtful feedback!