riichi/src/yaku.erl

87 lines
3.7 KiB
Erlang
Raw Normal View History

%% @author Correl Roush <correl@gmail.com>
%%
%% @doc Riichi Mahjong library.
2012-08-01 04:11:36 +00:00
%%
%% @headerfile "../include/riichi.hrl"
2012-07-21 01:09:28 +00:00
-module(yaku).
-include("../include/riichi.hrl").
2012-08-01 01:49:51 +00:00
-export([yakuhai/2,
tanyao/2,
pinfu/2,
chiitoitsu/2,
kokushi_musou/2]).
2012-07-21 01:09:28 +00:00
%% @doc Counts the pons/kans of value tiles in a player's hand.
%% Value tiles include all of the dragons, plus the round wind and the player's seat wind.
-spec yakuhai(game(), player()) -> integer().
2012-08-01 01:49:51 +00:00
yakuhai(#game{round=Round}, #player{seat=Seat, hand=#hand{melds=Melds}}) ->
2012-07-21 04:09:27 +00:00
length(lists:filter(fun(#meld{type=Type, tiles=[T|_]}) ->
case {Type, T} of
{pair, _} ->
false;
{chii, _} ->
false;
2012-08-01 01:49:51 +00:00
{_, #tile{suit=wind, value=Round}} ->
true;
{_, #tile{suit=wind, value=Seat}} ->
true;
2012-07-21 04:09:27 +00:00
{_, #tile{suit=dragon}} ->
2012-08-01 01:49:51 +00:00
true;
_ ->
false
2012-07-21 01:09:28 +00:00
end
end,
2012-07-21 04:09:27 +00:00
Melds)).
%% @doc Returns true if the hand consists only of simple tiles.
%% Terminals, winds and dragons are not allowed.
-spec tanyao(game(), player()) -> boolean().
2012-08-01 01:49:51 +00:00
tanyao(#game{}, #player{hand=Hand}) ->
2012-07-21 01:09:28 +00:00
not lists:any(fun(T = #tile{}) ->
case T#tile.suit of
dragon ->
true;
wind ->
true;
_ ->
lists:member(T#tile.value, [1,9])
end
end,
2012-08-01 01:49:51 +00:00
riichi_hand:tiles(Hand)).
2012-07-21 01:09:28 +00:00
%% @doc Returns true for a no-points hand.
%% To qualify for pinfu, the hand must be fully concealed, contain no pons/kans,
%% contain no dragons, round winds or seat winds, and must be won on an open wait.
-spec pinfu(game(), player()) -> boolean().
2012-08-01 02:35:32 +00:00
pinfu(#game{round=Round}, #player{seat=Seat, hand=Hand=#hand{melds=Melds}, drawn={_, Drawn}}) ->
2012-08-01 01:49:51 +00:00
Closed = lists:all(fun(T) -> T#tile.from =:= draw end, riichi_hand:tiles(Hand)),
2012-08-01 02:35:32 +00:00
OpenWait = length(riichi_hand:waits(#hand{tiles=riichi_hand:tiles(Hand) -- [Drawn]})) > 1,
2012-08-01 01:49:51 +00:00
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,
2012-08-01 02:35:32 +00:00
Closed and OpenWait and Chiis and NonValuePair.
2012-07-21 01:09:28 +00:00
%% @doc Returns true for a 7-pair hand.
-spec chiitoitsu(game(), player()) -> boolean().
2012-08-01 01:49:51 +00:00
chiitoitsu(#game{}, #player{hand=#hand{tiles=[], melds=Melds}})
2012-07-21 04:09:27 +00:00
when length(Melds) =:= 7 ->
Pairs = [S || S <- Melds, S#meld.type =:= pair],
2012-07-21 01:09:28 +00:00
length(Pairs) =:= 7 andalso sets:size(sets:from_list(Pairs)) =:= 7.
%% @doc Returns true for a 13 Orphans hand.
%% The hand must contain one each of every terminal and honour tile, plus one
%% additional tile matching any of the others in the hand.
-spec kokushi_musou(game(), player()) -> boolean().
2012-08-01 01:49:51 +00:00
kokushi_musou(#game{}, #player{hand=#hand{tiles=Tiles, melds=[#meld{type=pair, tiles=[T,T]}]}}) ->
2012-07-21 01:09:28 +00:00
not lists:any(fun(#tile{value=V}) ->
lists:member(V, lists:seq(2,8))
end,
2012-07-21 04:09:27 +00:00
[T|Tiles])
andalso sets:size(sets:from_list([T|Tiles])) =:= 13;
2012-08-01 01:49:51 +00:00
kokushi_musou(#game{}, #player{}) ->
false.