mirror of
https://github.com/correl/riichi.git
synced 2024-11-27 11:09:56 +00:00
New hand and yaku detection functions
This commit is contained in:
parent
c13705c2c2
commit
84fefa7ccb
4 changed files with 127 additions and 54 deletions
|
@ -5,7 +5,8 @@
|
||||||
|
|
||||||
-record(hand, {
|
-record(hand, {
|
||||||
tiles=[],
|
tiles=[],
|
||||||
sets=[]
|
sets=[],
|
||||||
|
seqs=[]
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-record(set, {
|
-record(set, {
|
||||||
|
|
|
@ -1,43 +1,67 @@
|
||||||
-module(riichi_hand).
|
-module(riichi_hand).
|
||||||
|
|
||||||
-include("riichi.hrl").
|
-include("../include/riichi.hrl").
|
||||||
|
|
||||||
-compile([export_all]).
|
-compile([export_all]).
|
||||||
|
|
||||||
|
find(Tiles) ->
|
||||||
|
find(lists:sort(Tiles), #hand{}, []).
|
||||||
|
|
||||||
find_sets(Tiles) ->
|
find([], Hand, Possible) ->
|
||||||
Unique = sets:to_list(sets:from_list(Tiles)),
|
[Hand|Possible];
|
||||||
[#set{count=length(lists:filter(fun(X) -> X == T end, Tiles)), tile=T, open=false}
|
|
||||||
|| T <- Unique].
|
|
||||||
|
|
||||||
reorder_seqs(Tiles) ->
|
find(Tiles, Hand = #hand{tiles=HT, sets=HS}, Possible) ->
|
||||||
Unique = sets:to_list(sets:from_list(Tiles)),
|
case Tiles of
|
||||||
lists:sort(Unique) ++ (Tiles -- Unique).
|
[T, T, T, T|Rest] ->
|
||||||
|
find(Rest, Hand#hand{sets=[#set{count=4, tile=T, open=false}|HS]}, Possible);
|
||||||
|
_ -> []
|
||||||
|
end ++
|
||||||
|
case Tiles of
|
||||||
|
[T, T, T|Rest] ->
|
||||||
|
find(Rest, Hand#hand{sets=[#set{count=3, tile=T, open=false}|HS]}, Possible);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end ++
|
||||||
|
case Tiles of
|
||||||
|
[T, T|Rest] ->
|
||||||
|
find(Rest, Hand#hand{sets=[#set{count=2, tile=T, open=false}|HS]}, Possible);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end ++
|
||||||
|
case lists:sort(sets:to_list(sets:from_list(Tiles))) of
|
||||||
|
[T1 = #tile{value=V1, suit=S}, T2 = #tile{value=V2, suit=S}, T3 = #tile{value=V3, suit=S}|_] when
|
||||||
|
is_integer(V1) andalso
|
||||||
|
[V1, V2, V3] =:= [V1, V1 + 1, V1 + 2] ->
|
||||||
|
find(Tiles -- [T1, T2, T3], Hand#hand{sets=[#seq{tiles=[T1, T2, T3], open=false}|HS]}, Possible);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end ++
|
||||||
|
case Tiles of
|
||||||
|
[T|Rest] ->
|
||||||
|
find(Rest, Hand#hand{tiles=[T|HT]}, Possible)
|
||||||
|
end.
|
||||||
|
|
||||||
find_seqs(Tiles) ->
|
is_complete(#hand{tiles=[], sets=Sets}) ->
|
||||||
find_seqs(reorder_seqs(Tiles), {[], []}).
|
Pairs = [S || S <- Sets, S#set.count =:= 2],
|
||||||
|
case length(Pairs) of
|
||||||
|
1 ->
|
||||||
|
% Four mentsu + 1 pair = 5 sets
|
||||||
|
length(Sets) =:= 5;
|
||||||
|
7 ->
|
||||||
|
% Must be seven *unique* pairs
|
||||||
|
sets:size(sets:from_list(Pairs)) =:= 7;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end;
|
||||||
|
is_complete(#hand{}=Hand) ->
|
||||||
|
kokushi_musou(Hand).
|
||||||
|
|
||||||
find_seqs([], {Seqs, Rest}) ->
|
% 13 Orphans
|
||||||
{lists:sort(Seqs), lists:sort(Rest)};
|
kokushi_musou(#hand{tiles=Tiles, sets=Sets}) when
|
||||||
|
length(Tiles) =:= 13
|
||||||
find_seqs([T1 = #tile{suit=Suit}, T2 = #tile{suit=Suit}, T3 = #tile{suit=Suit} | Tiles], {Seqs, Rest})
|
andalso length(Sets) =:= 0 ->
|
||||||
when T2#tile.value =:= (T1#tile.value + 1)
|
not lists:any(fun(#tile{value=V}) ->
|
||||||
andalso T3#tile.value =:= (T2#tile.value + 1) ->
|
lists:member(V, lists:seq(2,8))
|
||||||
find_seqs(reorder_seqs(Tiles), {[#seq{tiles=[T1, T2, T3], open=false} | Seqs], Rest});
|
end,
|
||||||
|
Tiles)
|
||||||
find_seqs([T | Tiles], {Seqs, Rest}) ->
|
andalso sets:size(sets:from_list(Tiles)) =:= 13.
|
||||||
find_seqs(Tiles, {Seqs, [T | Rest]}).
|
|
||||||
|
|
||||||
perms([]) -> [[]];
|
|
||||||
perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].
|
|
||||||
|
|
||||||
combinations(0, _) -> [[]];
|
|
||||||
combinations(_, []) -> [];
|
|
||||||
combinations(N, [X|XS]) -> [[X|YS] || YS <- combinations(N-1, XS)] ++ combinations(N, XS).
|
|
||||||
|
|
||||||
find_hands(Tiles) ->
|
|
||||||
find_hands(Tiles, []).
|
|
||||||
find_hands([], Hands) ->
|
|
||||||
Hands;
|
|
||||||
find_hands([_T | _Remaining] = Tiles, Hands) ->
|
|
||||||
[#hand{tiles=Tiles} | Hands].
|
|
||||||
|
|
61
src/yaku.erl
Normal file
61
src/yaku.erl
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
-module(yaku).
|
||||||
|
|
||||||
|
-include("../include/riichi.hrl").
|
||||||
|
|
||||||
|
-compile([export_all]).
|
||||||
|
|
||||||
|
|
||||||
|
yakuhai(#hand{sets=Sets}) ->
|
||||||
|
length(lists:filter(fun(T = #tile{}) ->
|
||||||
|
case T#tile.suit of
|
||||||
|
wind ->
|
||||||
|
% TODO: Round/Seat Winds
|
||||||
|
false;
|
||||||
|
dragon ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
Sets)).
|
||||||
|
|
||||||
|
tanyao(#hand{sets=Sets}) ->
|
||||||
|
not lists:any(fun(T = #tile{}) ->
|
||||||
|
case T#tile.suit of
|
||||||
|
dragon ->
|
||||||
|
true;
|
||||||
|
wind ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
lists:member(T#tile.value, [1,9])
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
Sets).
|
||||||
|
|
||||||
|
pinfu(#hand{sets=Sets}) ->
|
||||||
|
% TODO: Verify closed, open wait, pair not round/seat wind
|
||||||
|
lists:all(fun(S) ->
|
||||||
|
case S of
|
||||||
|
#seq{} ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
[S || S <- Sets]).
|
||||||
|
|
||||||
|
% 7 Pairs
|
||||||
|
chiitoitsu(#hand{tiles=[], sets=Sets})
|
||||||
|
when length(Sets) =:= 7 ->
|
||||||
|
Pairs = [S || S <- Sets, S#set.count =:= 2],
|
||||||
|
length(Pairs) =:= 7 andalso sets:size(sets:from_list(Pairs)) =:= 7.
|
||||||
|
|
||||||
|
% 13 Orphans
|
||||||
|
kokushi_musou(#hand{tiles=Tiles, sets=Sets})
|
||||||
|
when length(Tiles) =:= 13
|
||||||
|
andalso length(Sets) =:= 0 ->
|
||||||
|
not lists:any(fun(#tile{value=V}) ->
|
||||||
|
lists:member(V, lists:seq(2,8))
|
||||||
|
end,
|
||||||
|
Tiles)
|
||||||
|
andalso sets:size(sets:from_list(Tiles)) =:= 13.
|
|
@ -3,22 +3,9 @@
|
||||||
-include("riichi.hrl").
|
-include("riichi.hrl").
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
find_sets_test() ->
|
find_hands_test() ->
|
||||||
Tiles = [#tile{suit=man, value=V} || V <- lists:seq(1,8)],
|
Tiles = [#tile{value=N, suit=pin} || N <- [1,2,3], _ <- [1,2]],
|
||||||
Expected = {[
|
SetHand = #hand{sets=[#set{count=2, tile=#tile{value=N, suit=pin}, open=false} || N <- [3,2,1]]},
|
||||||
#seq{tiles=[#tile{suit=man, value=V} || V <- lists:seq(1,3)], open=false},
|
SeqHand = #hand{sets=[#seq{tiles=[#tile{value=N, suit=pin} || N <- [1,2,3]], open=false} || _ <- [1,2]]},
|
||||||
#seq{tiles=[#tile{suit=man, value=V} || V <- lists:seq(4,6)], open=false}
|
Found = [H || H <- riichi_hand:find(Tiles), H#hand.tiles =:= []],
|
||||||
],
|
?assertEqual([SetHand, SeqHand], Found).
|
||||||
[#tile{suit=man, value=V} || V <- lists:seq(7,8)]
|
|
||||||
},
|
|
||||||
?assertEqual(Expected, riichi_hand:find_seqs(Tiles)).
|
|
||||||
find_duplicate_sets_test() ->
|
|
||||||
Tiles = [#tile{suit=man, value=V} || V <- lists:seq(1,3), _ <- lists:seq(1,3)] ++
|
|
||||||
[#tile{suit=man, value=4} || _ <- [1,2]],
|
|
||||||
Expected = {[
|
|
||||||
#seq{tiles=[#tile{suit=man, value=V} || V <- lists:seq(1,3)], open=false}
|
|
||||||
|| _ <- [1,2,3]
|
|
||||||
],
|
|
||||||
[#tile{suit=man, value=4} || _ <- [1,2]]
|
|
||||||
},
|
|
||||||
?assertEqual(Expected, riichi_hand:find_seqs(Tiles)).
|
|
||||||
|
|
Loading…
Reference in a new issue