-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.