diff --git a/src/riichi_hand.erl b/src/riichi_hand.erl index 027d410..85ccce3 100644 --- a/src/riichi_hand.erl +++ b/src/riichi_hand.erl @@ -36,6 +36,17 @@ find(Tiles, Hand = #hand{tiles=HT, melds=HM}, Possible) -> find(Rest, Hand#hand{tiles=[T|HT]}, Possible) end. +tiles(#hand{tiles=Tiles, melds=Melds}) -> + lists:flatten([TS || #meld{tiles=TS} <- Melds]) ++ Tiles. + +head(#hand{melds=Melds}) -> + case [M || M = #meld{type=pair} <- Melds] of + [Pair|_] -> + Pair; + [] -> + none + end. + is_complete(#hand{tiles=[], melds=Melds}=Hand) -> Pairs = [M || M <- Melds, M#meld.type =:= pair], case length(Pairs) of diff --git a/src/yaku.erl b/src/yaku.erl index d3d31a2..50b6ed1 100644 --- a/src/yaku.erl +++ b/src/yaku.erl @@ -2,26 +2,32 @@ -include("../include/riichi.hrl"). --compile([export_all]). +-export([yakuhai/2, + tanyao/2, + pinfu/2, + chiitoitsu/2, + kokushi_musou/2]). - -yakuhai(#hand{melds=Melds}) -> +yakuhai(#game{round=Round}, #player{seat=Seat, hand=#hand{melds=Melds}}) -> length(lists:filter(fun(#meld{type=Type, tiles=[T|_]}) -> case {Type, T} of {pair, _} -> false; {chii, _} -> false; - {_, #tile{suit=wind}} -> - % TODO: Round/Seat Winds - false; + {_, #tile{suit=wind, value=Round}} -> + true; + {_, #tile{suit=wind, value=Seat}} -> + true; {_, #tile{suit=dragon}} -> - true + true; + _ -> + false end end, Melds)). -tanyao(#hand{melds=Melds}) -> +tanyao(#game{}, #player{hand=Hand}) -> not lists:any(fun(T = #tile{}) -> case T#tile.suit of dragon -> @@ -32,24 +38,30 @@ tanyao(#hand{melds=Melds}) -> lists:member(T#tile.value, [1,9]) end end, - lists:flatten([Tiles || #meld{tiles=Tiles} <- Melds])). + riichi_hand:tiles(Hand)). -pinfu(#hand{melds=Melds}) -> - % TODO: Verify closed, open wait, pair not round/seat wind - length([M || M = #meld{type=T} <- Melds, T =:= chii]) =:= 4. +pinfu(#game{round=Round}, #player{seat=Seat, hand=Hand=#hand{melds=Melds}}) -> + % TODO: Verify closed, open wait, and pair not round/seat wind + Closed = lists:all(fun(T) -> T#tile.from =:= draw end, riichi_hand:tiles(Hand)), + Chiis = length([M || M = #meld{type=chii} <- Melds]) =:= 4, + #meld{type=pair, tiles=[HeadTile,HeadTile]} = riichi_hand:head(Hand), + NonValuePair = HeadTile#tile.value =/= Round + andalso HeadTile#tile.value =/= Seat + andalso HeadTile#tile.suit =/= dragon, + Closed and Chiis and NonValuePair. % 7 Pairs -chiitoitsu(#hand{tiles=[], melds=Melds}) +chiitoitsu(#game{}, #player{hand=#hand{tiles=[], melds=Melds}}) when length(Melds) =:= 7 -> Pairs = [S || S <- Melds, S#meld.type =:= pair], length(Pairs) =:= 7 andalso sets:size(sets:from_list(Pairs)) =:= 7. % 13 Orphans -kokushi_musou(#hand{tiles=Tiles, melds=[#meld{type=pair, tiles=[T,T]}]}) -> +kokushi_musou(#game{}, #player{hand=#hand{tiles=Tiles, melds=[#meld{type=pair, tiles=[T,T]}]}}) -> not lists:any(fun(#tile{value=V}) -> lists:member(V, lists:seq(2,8)) end, [T|Tiles]) andalso sets:size(sets:from_list([T|Tiles])) =:= 13; -kokushi_musou(#hand{}) -> +kokushi_musou(#game{}, #player{}) -> false. diff --git a/test/riichi_hand_tests.erl b/test/riichi_hand_tests.erl deleted file mode 100644 index ad6816a..0000000 --- a/test/riichi_hand_tests.erl +++ /dev/null @@ -1,11 +0,0 @@ --module(riichi_hand_tests). - --include("riichi.hrl"). --include_lib("eunit/include/eunit.hrl"). - -find_hands_test() -> - Tiles = [#tile{value=N, suit=pin} || N <- [1,2,3], _ <- [1,2]], - SetHand = #hand{sets=[#set{count=2, tile=#tile{value=N, suit=pin}, open=false} || N <- [3,2,1]]}, - SeqHand = #hand{sets=[#seq{tiles=[#tile{value=N, suit=pin} || N <- [1,2,3]], open=false} || _ <- [1,2]]}, - Found = [H || H <- riichi_hand:find(Tiles), H#hand.tiles =:= []], - ?assertEqual([SetHand, SeqHand], Found). diff --git a/test/riichi_yaku_tests.erl b/test/riichi_yaku_tests.erl new file mode 100644 index 0000000..fdcc788 --- /dev/null +++ b/test/riichi_yaku_tests.erl @@ -0,0 +1,46 @@ +-module(riichi_yaku_tests). + +-compile(export_all). + +-include("../include/riichi.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +yakuhai_test_() -> + Yakuhai = [lists:duplicate(3, #tile{suit=wind, value=east}), + lists:duplicate(3, #tile{suit=wind, value=south}) + ] ++ [lists:duplicate(3, #tile{suit=dragon, value=V}) || V <- [red,white,green]], + Tiles = [#tile{suit=pin, value=V} || V <- [1,2] ++ lists:seq(1,9)], + Hands = [#hand{tiles=Tiles, melds=[#meld{type=pon, tiles=Y}]} || Y <- Yakuhai], + Players = [#player{seat=south, hand=H} || H <- Hands], + Games = [#game{round=east, players=[P]} || P <- Players], + [?_assertEqual(1, yaku:yakuhai(G, P)) || G = #game{players=[P]} <- Games]. + +multiple_yakuhai_test() -> + Yakuhai = [#meld{type=pon, tiles=lists:duplicate(3, #tile{suit=dragon, value=D})} || D <- [red,green,white]], + Player = #player{seat=south, hand=#hand{melds=Yakuhai}}, + ?assertEqual(3, yaku:yakuhai(#game{}, Player)). + +tanyao_test() -> + Hand = #hand{melds=[#meld{type=pon, tiles=lists:duplicate(3, #tile{suit=pin, value=2})}, + #meld{type=pair, tiles=lists:duplicate(2, #tile{suit=sou, value=8})}, + #meld{type=chii, tiles=[#tile{suit=sou, value=V} || V <- [4,5,6]]}, + #meld{type=chii, tiles=[#tile{suit=sou, value=V} || V <- [4,5,6]]}, + #meld{type=chii, tiles=[#tile{suit=man, value=V} || V <- [6,7,8]]}]}, + ?assertEqual(true, yaku:tanyao(#game{}, #player{hand=Hand})). + +pinfu_test() -> + Hand = #hand{melds=[#meld{type=pair, tiles=lists:duplicate(2, #tile{suit=sou, value=8})}, + #meld{type=chii, tiles=[#tile{suit=pin, value=V} || V <- [1,2,3]]}, + #meld{type=chii, tiles=[#tile{suit=sou, value=V} || V <- [4,5,6]]}, + #meld{type=chii, tiles=[#tile{suit=sou, value=V} || V <- [4,5,6]]}, + #meld{type=chii, tiles=[#tile{suit=man, value=V} || V <- [6,7,8]]}]}, + ?assertEqual(true, yaku:pinfu(#game{}, #player{hand=Hand})). + +chiitoitsu_test() -> + Hand = #hand{melds=[#meld{type=pair, tiles=lists:duplicate(2, #tile{suit=pin, value=V})} || V <- lists:seq(1,7)]}, + ?assertEqual(true, yaku:chiitoitsu(#game{}, #player{hand=Hand})). + +kokushi_musou_test() -> + Hand = #hand{tiles=?TERMINALS ++ ?HONOURS -- [#tile{suit=pin, value=1}], + melds=[#meld{type=pair, tiles=lists:duplicate(2, #tile{suit=pin, value=1})}]}, + ?assertEqual(true, yaku:kokushi_musou(#game{}, #player{hand=Hand})).