Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

170 linhas
5.1 KiB

2 meses atrás
  1. -module(game_logic).
  2. -export([validate_play/2, calculate_card_value/1, evaluate_hand_strength/1,
  3. find_all_possible_patterns/1, analyze_card_pattern/1]).
  4. -define(CARD_VALUES, #{
  5. "3" => 3, "4" => 4, "5" => 5, "6" => 6, "7" => 7, "8" => 8, "9" => 9,
  6. "10" => 10, "J" => 11, "Q" => 12, "K" => 13, "A" => 14, "2" => 15,
  7. "小王" => 16, "大王" => 17
  8. }).
  9. %% 牌型定义
  10. -record(card_pattern, {
  11. type, % single | pair | triple | triple_with_one | straight |
  12. % straight_pair | airplane | airplane_with_wings | four_with_two | bomb | rocket
  13. value, % 主牌值
  14. length = 1, % 顺子长度
  15. extra = [] % 附加牌
  16. }).
  17. %% 验证出牌是否合法
  18. validate_play(Cards, LastPlay) ->
  19. case analyze_card_pattern(Cards) of
  20. {invalid, _} ->
  21. false;
  22. {Pattern, Value} ->
  23. case analyze_card_pattern(LastPlay) of
  24. {Pattern, LastValue} when Value > LastValue ->
  25. true;
  26. {_, _} ->
  27. check_special_cases(Pattern, Value, LastPlay);
  28. _ ->
  29. false
  30. end
  31. end.
  32. %% 分析牌型
  33. analyze_card_pattern(Cards) ->
  34. Grouped = group_cards(Cards),
  35. case identify_pattern(Grouped) of
  36. {Type, Value} when Type =/= invalid ->
  37. {#card_pattern{type = Type, value = Value}, Value};
  38. _ ->
  39. {invalid, 0}
  40. end.
  41. %% 计算手牌价值
  42. calculate_card_value(Cards) ->
  43. {Pattern, BaseValue} = analyze_card_pattern(Cards),
  44. case Pattern#card_pattern.type of
  45. bomb -> BaseValue * 2;
  46. rocket -> 1000;
  47. _ -> BaseValue
  48. end.
  49. %% 评估手牌强度
  50. evaluate_hand_strength(Cards) ->
  51. Patterns = find_all_possible_patterns(Cards),
  52. BaseScore = lists:foldl(
  53. fun(Pattern, Acc) ->
  54. Acc + calculate_pattern_value(Pattern)
  55. end,
  56. 0,
  57. Patterns
  58. ),
  59. adjust_score_by_combination(BaseScore, Cards).
  60. %% 找出所有可能的牌型组合
  61. find_all_possible_patterns(Cards) ->
  62. Grouped = group_cards(Cards),
  63. Singles = find_singles(Grouped),
  64. Pairs = find_pairs(Grouped),
  65. Triples = find_triples(Grouped),
  66. Straights = find_straights(Cards),
  67. StraightPairs = find_straight_pairs(Grouped),
  68. Airplanes = find_airplanes(Grouped),
  69. Bombs = find_bombs(Grouped),
  70. Rockets = find_rockets(Cards),
  71. Singles ++ Pairs ++ Triples ++ Straights ++ StraightPairs ++
  72. Airplanes ++ Bombs ++ Rockets.
  73. %% 内部辅助函数
  74. group_cards(Cards) ->
  75. lists:foldl(
  76. fun({_, Number}, Acc) ->
  77. maps:update_with(
  78. Number,
  79. fun(Count) -> Count + 1 end,
  80. 1,
  81. Acc
  82. )
  83. end,
  84. #{},
  85. Cards
  86. ).
  87. identify_pattern(Grouped) ->
  88. case maps:size(Grouped) of
  89. 1 ->
  90. [{Number, Count}] = maps:to_list(Grouped),
  91. case Count of
  92. 1 -> {single, maps:get(Number, ?CARD_VALUES)};
  93. 2 -> {pair, maps:get(Number, ?CARD_VALUES)};
  94. 3 -> {triple, maps:get(Number, ?CARD_VALUES)};
  95. 4 -> {bomb, maps:get(Number, ?CARD_VALUES)};
  96. _ -> {invalid, 0}
  97. end;
  98. _ ->
  99. identify_complex_pattern(Grouped)
  100. end.
  101. identify_complex_pattern(Grouped) ->
  102. case find_straight_pattern(Grouped) of
  103. {ok, Pattern} -> Pattern;
  104. false ->
  105. case find_airplane_pattern(Grouped) of
  106. {ok, Pattern} -> Pattern;
  107. false ->
  108. case find_four_with_two(Grouped) of
  109. {ok, Pattern} -> Pattern;
  110. false -> {invalid, 0}
  111. end
  112. end
  113. end.
  114. find_straight_pattern(Grouped) ->
  115. Numbers = lists:sort(maps:keys(Grouped)),
  116. case is_consecutive(Numbers) of
  117. true when length(Numbers) >= 5 ->
  118. MaxValue = lists:max([maps:get(N, ?CARD_VALUES) || N <- Numbers]),
  119. {ok, {straight, MaxValue}};
  120. _ ->
  121. false
  122. end.
  123. is_consecutive(Numbers) ->
  124. case Numbers of
  125. [] -> true;
  126. [_] -> true;
  127. [First|Rest] ->
  128. lists:all(
  129. fun({A, B}) -> maps:get(B, ?CARD_VALUES) - maps:get(A, ?CARD_VALUES) =:= 1 end,
  130. lists:zip(Numbers, Rest)
  131. )
  132. end.
  133. calculate_pattern_value(Pattern) ->
  134. case Pattern of
  135. #card_pattern{type = single, value = V} -> V;
  136. #card_pattern{type = pair, value = V} -> V * 2;
  137. #card_pattern{type = triple, value = V} -> V * 3;
  138. #card_pattern{type = bomb, value = V} -> V * 4;
  139. #card_pattern{type = rocket} -> 1000;
  140. #card_pattern{type = straight, value = V, length = L} -> V * L;
  141. _ -> 0
  142. end.
  143. adjust_score_by_combination(BaseScore, Cards) ->
  144. CombinationBonus = case length(Cards) of
  145. N when N =< 5 -> BaseScore * 1.2;
  146. N when N =< 10 -> BaseScore * 1.1;
  147. _ -> BaseScore
  148. end,
  149. round(CombinationBonus).
  150. check_special_cases(Pattern, Value, LastPlay) ->
  151. case Pattern#card_pattern.type of
  152. bomb -> true;
  153. rocket -> true;
  154. _ -> false
  155. end.