Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

171 строки
5.1 KiB

-module(game_logic).
-export([validate_play/2, calculate_card_value/1, evaluate_hand_strength/1,
find_all_possible_patterns/1, analyze_card_pattern/1]).
-define(CARD_VALUES, #{
"3" => 3, "4" => 4, "5" => 5, "6" => 6, "7" => 7, "8" => 8, "9" => 9,
"10" => 10, "J" => 11, "Q" => 12, "K" => 13, "A" => 14, "2" => 15,
"小王" => 16, "大王" => 17
}).
%% 牌型定义
-record(card_pattern, {
type, % single | pair | triple | triple_with_one | straight |
% straight_pair | airplane | airplane_with_wings | four_with_two | bomb | rocket
value, % 主牌值
length = 1, % 顺子长度
extra = [] % 附加牌
}).
%% 验证出牌是否合法
validate_play(Cards, LastPlay) ->
case analyze_card_pattern(Cards) of
{invalid, _} ->
false;
{Pattern, Value} ->
case analyze_card_pattern(LastPlay) of
{Pattern, LastValue} when Value > LastValue ->
true;
{_, _} ->
check_special_cases(Pattern, Value, LastPlay);
_ ->
false
end
end.
%% 分析牌型
analyze_card_pattern(Cards) ->
Grouped = group_cards(Cards),
case identify_pattern(Grouped) of
{Type, Value} when Type =/= invalid ->
{#card_pattern{type = Type, value = Value}, Value};
_ ->
{invalid, 0}
end.
%% 计算手牌价值
calculate_card_value(Cards) ->
{Pattern, BaseValue} = analyze_card_pattern(Cards),
case Pattern#card_pattern.type of
bomb -> BaseValue * 2;
rocket -> 1000;
_ -> BaseValue
end.
%% 评估手牌强度
evaluate_hand_strength(Cards) ->
Patterns = find_all_possible_patterns(Cards),
BaseScore = lists:foldl(
fun(Pattern, Acc) ->
Acc + calculate_pattern_value(Pattern)
end,
0,
Patterns
),
adjust_score_by_combination(BaseScore, Cards).
%% 找出所有可能的牌型组合
find_all_possible_patterns(Cards) ->
Grouped = group_cards(Cards),
Singles = find_singles(Grouped),
Pairs = find_pairs(Grouped),
Triples = find_triples(Grouped),
Straights = find_straights(Cards),
StraightPairs = find_straight_pairs(Grouped),
Airplanes = find_airplanes(Grouped),
Bombs = find_bombs(Grouped),
Rockets = find_rockets(Cards),
Singles ++ Pairs ++ Triples ++ Straights ++ StraightPairs ++
Airplanes ++ Bombs ++ Rockets.
%% 内部辅助函数
group_cards(Cards) ->
lists:foldl(
fun({_, Number}, Acc) ->
maps:update_with(
Number,
fun(Count) -> Count + 1 end,
1,
Acc
)
end,
#{},
Cards
).
identify_pattern(Grouped) ->
case maps:size(Grouped) of
1 ->
[{Number, Count}] = maps:to_list(Grouped),
case Count of
1 -> {single, maps:get(Number, ?CARD_VALUES)};
2 -> {pair, maps:get(Number, ?CARD_VALUES)};
3 -> {triple, maps:get(Number, ?CARD_VALUES)};
4 -> {bomb, maps:get(Number, ?CARD_VALUES)};
_ -> {invalid, 0}
end;
_ ->
identify_complex_pattern(Grouped)
end.
identify_complex_pattern(Grouped) ->
case find_straight_pattern(Grouped) of
{ok, Pattern} -> Pattern;
false ->
case find_airplane_pattern(Grouped) of
{ok, Pattern} -> Pattern;
false ->
case find_four_with_two(Grouped) of
{ok, Pattern} -> Pattern;
false -> {invalid, 0}
end
end
end.
find_straight_pattern(Grouped) ->
Numbers = lists:sort(maps:keys(Grouped)),
case is_consecutive(Numbers) of
true when length(Numbers) >= 5 ->
MaxValue = lists:max([maps:get(N, ?CARD_VALUES) || N <- Numbers]),
{ok, {straight, MaxValue}};
_ ->
false
end.
is_consecutive(Numbers) ->
case Numbers of
[] -> true;
[_] -> true;
[First|Rest] ->
lists:all(
fun({A, B}) -> maps:get(B, ?CARD_VALUES) - maps:get(A, ?CARD_VALUES) =:= 1 end,
lists:zip(Numbers, Rest)
)
end.
calculate_pattern_value(Pattern) ->
case Pattern of
#card_pattern{type = single, value = V} -> V;
#card_pattern{type = pair, value = V} -> V * 2;
#card_pattern{type = triple, value = V} -> V * 3;
#card_pattern{type = bomb, value = V} -> V * 4;
#card_pattern{type = rocket} -> 1000;
#card_pattern{type = straight, value = V, length = L} -> V * L;
_ -> 0
end.
adjust_score_by_combination(BaseScore, Cards) ->
CombinationBonus = case length(Cards) of
N when N =< 5 -> BaseScore * 1.2;
N when N =< 10 -> BaseScore * 1.1;
_ -> BaseScore
end,
round(CombinationBonus).
check_special_cases(Pattern, Value, LastPlay) ->
case Pattern#card_pattern.type of
bomb -> true;
rocket -> true;
_ -> false
end.