Last active
March 20, 2017 19:30
-
-
Save paucus/58eed0168c028313445d83e5e71cee82 to your computer and use it in GitHub Desktop.
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
%%% assignment 3.12 Functional Programming in Erlang, 2017 | |
-module(rps). | |
-export([play/1,echo/1,play_two/3,rock/1,no_repeat/1,const/1,enum/1,cycle/1,rand/1,val/1,tournament/2]). | |
-export([type_count/1, least_freq_played/1,most_freq_played/1,rand_strategy/1]). | |
-created_by("Marcelo Ruiz Camauër"). | |
-include_lib("eunit/include/eunit.hrl"). | |
% | |
% interactively play against a strategy, provided as argument. | |
% example: | |
% rps:play(fun rps:rand/1). | |
% | |
play(Strategy) -> | |
% play interactively until user enters "stop" | |
io:format("Rock - paper - scissors~n"), | |
io:format("Play one of rock (r), paper (p), scissors (s), or 'stop'. ~n"), | |
play(Strategy,[]). | |
% tail recursive loop for play/1: | |
play(Strategy,Moves) -> | |
{ok,P} = io:read("Your play: "), | |
Play = expand(P), | |
case Play of | |
stop -> | |
io:format("Stopped~n"); | |
_ -> % should have something here to catch illegal input... | |
OponentPlay = Strategy(Moves), | |
Result = result(Play,OponentPlay), | |
io:format("Result: ~p vs ~p, you ~p~n",[Play,OponentPlay,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. | |
type_count(L) -> % by Callous M. | |
FilterFn = fun(Type) -> fun(X) when X =:= Type -> true; (_) -> false end end, | |
R = {_, rock} = {length(lists:filter(FilterFn(rock), L)), rock}, | |
P = {_, paper} = {length(lists:filter(FilterFn(paper), L)), paper}, | |
S = {_, scissors} = {length(lists:filter(FilterFn(scissors), L)), scissors}, | |
{R, P, S}. | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% STRATEGIES: | |
% Add file strategies that when passed a list of previous moves by the opponent, will determine what to play: | |
% | |
% * assume that your opponent never repeats herself: if you know this you can make a choice that will never lose; | |
% * make a random choice each time; you may want to use the random:uniform/1 function so that | |
% random:uniform(N) returns a random choice of 1,2, … N with equal probability each time; | |
% * cycles through the three choices in some order; | |
% * apply an analysis to the previous plays and choose the least frequent, assuming that in the long run your | |
% opponent will play each choice equally; | |
% * apply an analysis to the previous plays and choose the most frequent, assuming that in the long run your | |
% opponent is going to play that choice more often than the others. | |
% | |
% We can also define functions that combine strategies and return strategies: | |
% * Define a strategy that takes a list of strategies and each play chooses a random one to apply. | |
% * Define a strategy that takes a list of strategies and each play chooses from the list the strategy | |
% which gets the best result when played against the list of plays made so far. | |
% | |
echo([]) -> | |
paper; | |
echo([Last|_]) -> | |
% play whatever the opponent played last. | |
Last. | |
rock(_) -> | |
% play always 'rock', no matter previous opponents moves | |
rock. | |
no_repeat([]) -> | |
% play something that does not repeat the opponent's last play: | |
paper; | |
no_repeat([X|_Xs]) -> | |
case X of | |
rock -> scissors; | |
paper -> rock; | |
scissors -> paper | |
end. | |
const(_Xs) -> | |
% play always the same choice | |
paper. | |
cycle(Xs) -> | |
% cycles through the three choices in sequence | |
enum(length(Xs) rem 3). | |
rand(_) -> | |
% choice made at random | |
enum(rand:uniform(3)-1). | |
least_freq_played(Xs) -> | |
{R,P,S} = type_count(Xs), | |
{_, T} = min(R, min(P, S)), | |
T. | |
rand_strategy(Xs)-> | |
Choice = rand:uniform(8), | |
%io:format("strategy ~p ~n",[Choice]), | |
case Choice of | |
1->least_freq_played(Xs); | |
2->most_freq_played(Xs); | |
3->rand(Xs); | |
4->rock(Xs); | |
5->cycle(Xs); | |
6->no_repeat(Xs); | |
7->echo(Xs); | |
8->rock(Xs) | |
end. | |
% most_freq | |
most_freq_played(Xs) -> | |
{R,P,S} = type_count(Xs), | |
{_, T} = max(R, max(P, S)), | |
T. | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% Strategy vs Strategy | |
% Define a function that takes three arguments: two strategies and a number N, and which plays | |
% the strategies against each other for N turns. At each stage the function should output the result of the | |
% round, and it should show the result of the tournament at the end. | |
% | |
% play one strategy against another, for N moves. | |
% example: | |
% rps:play_two(fun rps:rand/1,fun rps:rand/1,30). | |
% rps:play_two(fun rps:least_freq_played/1,fun rps:no_repeat/1,30). | |
% rps:play_two(fun rps:least_freq_played/1,fun rps:most_freq_played/1,30). | |
% rps:play_two(fun rps:rand_strategy/1,fun rps:rand_strategy/1,30). | |
play_two(StrategyL,StrategyR,N) -> | |
% given a strategy function for each player, play them out for N moves | |
play_two(StrategyL,StrategyR,[],[],N). | |
% tail recursive loop for play_two/3 | |
% 0 case computes the result of the tournament | |
play_two(_,_,PlaysL,PlaysR,0) -> | |
% if we played out all the moves, report the final result | |
io:format("Final score for left player: ~p~n", [tournament(PlaysL, PlaysR)]); | |
play_two(StrategyL,StrategyR,PlaysL,PlaysR,N) -> | |
PlayL = StrategyL(PlaysR), | |
PlayR = StrategyR(PlaysL), | |
Result = result(PlayL, PlayR), | |
case Result of | |
draw -> io:format("turn ~p ~p vs ~p, ~p.~n", [N,PlayL, PlayR, Result]); | |
_ -> io:format("turn ~p ~p vs ~p, ~p for left player.~n", [N,PlayL, PlayR, Result]) | |
end, | |
case abs(tournament(PlaysL, PlaysR)) > 10 of % if winning or losing by a lot, end the game | |
true -> MovesLeft = 1 ; | |
false-> MovesLeft = N | |
end, | |
play_two(StrategyL, StrategyR, [PlayL | PlaysL], [PlayR | PlaysR], MovesLeft - 1). | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% tests | |
result_test() -> | |
?assertEqual(result(rock,rock), draw), | |
?assertEqual(result(rock,paper), lose), | |
?assertEqual(result(rock,scissors), win), | |
?assertEqual(result(paper,rock), win), | |
?assertEqual(result(paper,paper), draw), | |
?assertEqual(result(paper,scissors), lose), | |
?assertEqual(result(scissors,rock), lose), | |
?assertEqual(result(scissors,paper), win), | |
?assertEqual(result(scissors,scissors), draw). | |
tournament_test() -> | |
?assertEqual(tournament([rock],[rock]), 0), | |
?assertEqual(tournament([rock,rock],[rock,paper]), -1), | |
?assertEqual(tournament([rock,rock,paper],[rock,paper,scissors]), -2), | |
?assertEqual(tournament([rock,rock,paper,paper],[rock,paper,scissors,rock]), -1). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment