commit a68168d6517faaa15e13aaf9d7b4d9aa658b9797 Author: Correl Roush Date: Fri May 4 08:18:08 2012 -0400 Initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c62f248 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +.PHONY: all deps compile test clean + +REBAR=./rebar + +all: deps compile + +deps: + @$(REBAR) get-deps +compile: deps + @$(REBAR) compile +test: + @$(REBAR) skip_deps=true eunit +clean: + @$(REBAR) clean diff --git a/include/riichi.hrl b/include/riichi.hrl new file mode 100644 index 0000000..719a458 --- /dev/null +++ b/include/riichi.hrl @@ -0,0 +1,4 @@ +-record(tile, { + suit, + value +}). diff --git a/rebar b/rebar new file mode 100755 index 0000000..672a9f8 Binary files /dev/null and b/rebar differ diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..4483aa9 --- /dev/null +++ b/rebar.config @@ -0,0 +1 @@ +{cover_enabled, true}. diff --git a/src/riichi.app.src b/src/riichi.app.src new file mode 100644 index 0000000..9197eeb --- /dev/null +++ b/src/riichi.app.src @@ -0,0 +1,12 @@ +{application, riichi, + [ + {description, ""}, + {vsn, "1"}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {mod, { riichi_app, []}}, + {env, []} + ]}. diff --git a/src/riichi.erl b/src/riichi.erl new file mode 100644 index 0000000..f9f3738 --- /dev/null +++ b/src/riichi.erl @@ -0,0 +1,73 @@ +-module(riichi). + +-include("riichi.hrl"). + +-export([ + is_valid_tile/1, + dora/1, + nearest/2, + score/3 +]). + +is_valid_tile(#tile{suit=dragon, value=Value}) -> + lists:member(Value, [white, green, red]); +is_valid_tile(#tile{suit=wind, value=Value}) -> + lists:member(Value, [east, south, west, north]); +is_valid_tile(#tile{suit=Suit, value=Value}) -> + ValidSuit = lists:member(Suit, [pin, sou, man]), + ValidValue = is_integer(Value) and (Value > 0) and (Value < 10), + ValidSuit and ValidValue; +is_valid_tile(_) -> + false. + +dora(#tile{suit=dragon, value=Value}=Indicator) -> + case Value of + white -> Indicator#tile{value=green}; + green -> Indicator#tile{value=red}; + red -> Indicator#tile{value=white} + end; +dora(#tile{suit=wind, value=Value}=Indicator) -> + case Value of + east -> Indicator#tile{value=south}; + south -> Indicator#tile{value=west}; + west -> Indicator#tile{value=north}; + north -> Indicator#tile{value=east} + end; +dora(#tile{value=Value}=Indicator) -> + case is_valid_tile(Indicator) of + false -> + throw({error, invalid_tile}); + _ -> + if + Value == 9 -> Indicator#tile{value=1}; + true -> Indicator#tile{value=Value + 1} + end + end. + +nearest(Num, To) when Num rem To == 0 -> + Num; +nearest(Num, To) -> + Num - (Num rem To) + To. + + +score(_Fu, Yaku, Limit) when (Yaku >= 5) and (Limit == true) -> + if + Yaku < 6 -> + 2000; + Yaku < 8 -> + 3000; + Yaku < 11 -> + 4000; + Yaku < 14 -> + 6000; + true -> + 8000 + end; +score(Fu, Yaku, Limit) -> + Score = nearest(Fu * round(math:pow(2, 2 + Yaku)), 100), + if + Limit and (Score > 2000) -> + 2000; + true -> + Score + end. diff --git a/src/riichi_app.erl b/src/riichi_app.erl new file mode 100644 index 0000000..cf619f5 --- /dev/null +++ b/src/riichi_app.erl @@ -0,0 +1,16 @@ +-module(riichi_app). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +%% =================================================================== +%% Application callbacks +%% =================================================================== + +start(_StartType, _StartArgs) -> + riichi_sup:start_link(). + +stop(_State) -> + ok. diff --git a/src/riichi_sup.erl b/src/riichi_sup.erl new file mode 100644 index 0000000..1095032 --- /dev/null +++ b/src/riichi_sup.erl @@ -0,0 +1,28 @@ + +-module(riichi_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%% Helper macro for declaring children of supervisor +-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). + +%% =================================================================== +%% API functions +%% =================================================================== + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% =================================================================== +%% Supervisor callbacks +%% =================================================================== + +init([]) -> + {ok, { {one_for_one, 5, 10}, []} }. + diff --git a/test/riichi_tests.erl b/test/riichi_tests.erl new file mode 100644 index 0000000..147f693 --- /dev/null +++ b/test/riichi_tests.erl @@ -0,0 +1,84 @@ +-module(riichi_tests). + +-include("riichi.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +valid_tile_dragon_test_() -> + [?_assert(riichi:is_valid_tile(#tile{suit=dragon, value=Value})) || Value <- [white, green, red]]. +valid_tile_wind_test_() -> + [?_assert(riichi:is_valid_tile(#tile{suit=wind, value=Value})) || Value <- [east, south, west, north]]. +valid_tile_suit_test_() -> + [?_assert(riichi:is_valid_tile(#tile{suit=Suit, value=Value})) || Suit <- [pin, sou, man], Value <- lists:seq(1,9)]. + +invalid_tile_test_() -> + [?_assertNot(riichi:is_valid_tile(Tile)) + || Tile <- + [ + #tile{suit=badsuit, value=1}, + #tile{suit=dragon, value=hidden}, + #tile{suit=wind, value=broken}, + #tile{suit=pin, value=0}, + #tile{suit=sou, value=-1}, + #tile{suit=man, value=10}, + not_a_tile + ] + ]. + +dora_dragon_test_() -> + [?_assertEqual(riichi:dora(#tile{suit=dragon, value=Indicator}), #tile{suit=dragon, value=Dora}) + || {Indicator, Dora} <- + [ + {white, green}, + {green, red}, + {red, white} + ] + ]. + +dora_wind_test_() -> + [?_assertEqual(riichi:dora(#tile{suit=wind, value=Indicator}), #tile{suit=wind, value=Dora}) + || {Indicator, Dora} <- + [ + {east, south}, + {south, west}, + {west, north}, + {north, east} + ] + ]. + +dora_suit_test_() -> + [?_assertEqual(riichi:dora(#tile{suit=Suit, value=Indicator}), #tile{suit=Suit, value=Dora}) + || + {Indicator, Dora} <- + [{1,2}, {2,3}, {3,4}, {4,5}, {5,6}, {6,7}, {7,8}, {8,9}, {9,1}], + Suit <- [pin, sou, man] + ]. + +dora_invalid_test() -> + ?assertException(throw, {error, invalid_tile}, riichi:dora(#tile{suit=pin, value=9001})). + +nearest_test_() -> + [?_assertEqual(riichi:nearest(Val, 10), 80) || Val <- lists:seq(71,80)]. + +score_yaku_limit_test_() -> + [?_assertEqual(riichi:score(30, Yaku, true), Score) + || {Yaku, Score} <- + [ + { 5, 2000}, + { 6, 3000}, + { 7, 3000}, + { 8, 4000}, + { 9, 4000}, + {10, 4000}, + {11, 6000}, + {12, 6000}, + {13, 6000}, + {14, 8000}, + {1000, 8000} + ] + ]. + +score_fu_limit_test() -> + ?assertEqual(riichi:score(80, 4, true), 2000). + +score_rounded_test() -> + ?assertEqual(riichi:score(20, 1, true), 200). % 160 rounded up