|
|
@ -8,7 +8,8 @@ |
|
|
|
analyze_hand_value/1 |
|
|
|
]). |
|
|
|
|
|
|
|
-include("card_types.hrl"). |
|
|
|
-include("../include/card_types.hrl"). |
|
|
|
-include("../include/game_records.hrl"). |
|
|
|
|
|
|
|
-record(strategy_context, { |
|
|
|
game_stage, % early_game | mid_game | end_game |
|
|
@ -68,6 +69,102 @@ execute_strategy(Strategy, GameState, PlayerState) -> |
|
|
|
|
|
|
|
%% 内部函数 |
|
|
|
|
|
|
|
%% 获取对手剩余牌数 |
|
|
|
get_opponent_cards(GameState) -> |
|
|
|
Players = GameState#game_state.players, |
|
|
|
lists:foldl(fun({_Pid, Cards, _Role}, Acc) -> |
|
|
|
Acc + length(Cards) |
|
|
|
end, 0, Players). |
|
|
|
|
|
|
|
%% 获取最后一手牌 |
|
|
|
get_last_play(GameState) -> |
|
|
|
case GameState#game_state.last_play of |
|
|
|
[] -> none; |
|
|
|
{_Pid, Cards} -> Cards |
|
|
|
end. |
|
|
|
|
|
|
|
%% 确定游戏阶段 |
|
|
|
determine_game_stage(GameState) -> |
|
|
|
% 根据已出牌数量和总牌数判断游戏阶段 |
|
|
|
PlayedCards = GameState#game_state.played_cards, |
|
|
|
TotalPlayed = lists:foldl(fun({_Pid, Cards}, Acc) -> |
|
|
|
Acc + length(Cards) |
|
|
|
end, 0, PlayedCards), |
|
|
|
|
|
|
|
% 假设总牌数为54张 |
|
|
|
case TotalPlayed of |
|
|
|
N when N < 18 -> early_game; |
|
|
|
N when N < 36 -> mid_game; |
|
|
|
_ -> end_game |
|
|
|
end. |
|
|
|
|
|
|
|
%% 评估手牌强度 |
|
|
|
evaluate_hand_strength(HandCards) -> |
|
|
|
HandValue = analyze_hand_value(HandCards), |
|
|
|
|
|
|
|
% 计算各种牌型的权重得分 |
|
|
|
SinglesScore = length(HandValue#hand_value.singles) * 0.1, |
|
|
|
PairsScore = length(HandValue#hand_value.pairs) * 0.2, |
|
|
|
TriplesScore = length(HandValue#hand_value.triples) * 0.3, |
|
|
|
SequencesScore = length(HandValue#hand_value.sequences) * 0.4, |
|
|
|
BombsScore = length(HandValue#hand_value.bombs) * 0.6, |
|
|
|
RocketsScore = length(HandValue#hand_value.rockets) * 1.0, |
|
|
|
|
|
|
|
% 归一化到0-1范围 |
|
|
|
TotalScore = SinglesScore + PairsScore + TriplesScore + SequencesScore + BombsScore + RocketsScore, |
|
|
|
NormalizedScore = min(1.0, TotalScore / 10), |
|
|
|
NormalizedScore. |
|
|
|
|
|
|
|
%% 计算控制水平 |
|
|
|
calculate_control_level(GameState, PlayerState) -> |
|
|
|
% 根据手牌中的高牌和炸弹数量评估控制能力 |
|
|
|
HandCards = PlayerState#state.hand_cards, |
|
|
|
HandValue = analyze_hand_value(HandCards), |
|
|
|
|
|
|
|
% 计算高牌数量(2和大小王) |
|
|
|
HighCards = lists:filter(fun({Value, _}) -> |
|
|
|
Value >= ?CARD_VALUE_2 |
|
|
|
end, HandCards), |
|
|
|
|
|
|
|
% 计算控制得分 |
|
|
|
HighCardScore = length(HighCards) * 0.2, |
|
|
|
BombScore = length(HandValue#hand_value.bombs) * 0.4, |
|
|
|
RocketScore = length(HandValue#hand_value.rockets) * 0.4, |
|
|
|
|
|
|
|
% 归一化到0-1范围 |
|
|
|
ControlScore = HighCardScore + BombScore + RocketScore, |
|
|
|
min(1.0, ControlScore). |
|
|
|
|
|
|
|
%% 估计胜率 |
|
|
|
estimate_winning_probability(GameState, PlayerState) -> |
|
|
|
% 简单估计:根据手牌数量和强度 |
|
|
|
HandCards = PlayerState#state.hand_cards, |
|
|
|
HandStrength = evaluate_hand_strength(HandCards), |
|
|
|
CardsRemaining = length(HandCards), |
|
|
|
|
|
|
|
% 获取对手剩余牌数 |
|
|
|
OpponentCards = get_opponent_cards(GameState), |
|
|
|
|
|
|
|
% 计算牌数优势 |
|
|
|
CardAdvantage = case PlayerState#state.role of |
|
|
|
dizhu -> |
|
|
|
% 地主初始多3张牌,所以需要调整 |
|
|
|
case OpponentCards of |
|
|
|
0 -> 1.0; % 对手没牌了,必输 |
|
|
|
_ -> max(0, 1.0 - (CardsRemaining / OpponentCards)) |
|
|
|
end; |
|
|
|
nongmin -> |
|
|
|
% 农民需要合作,所以计算方式不同 |
|
|
|
case CardsRemaining of |
|
|
|
0 -> 1.0; % 自己没牌了,必赢 |
|
|
|
_ -> max(0, 1.0 - (CardsRemaining / max(1, OpponentCards))) |
|
|
|
end |
|
|
|
end, |
|
|
|
|
|
|
|
% 综合手牌强度和牌数优势 |
|
|
|
WinProb = HandStrength * 0.6 + CardAdvantage * 0.4, |
|
|
|
min(1.0, WinProb). |
|
|
|
|
|
|
|
%% 选择基础策略 |
|
|
|
choose_base_strategy(Context) -> |
|
|
|
case {Context#strategy_context.game_stage, Context#strategy_context.role} of |
|
|
@ -263,137 +360,312 @@ find_triples(GroupedCards) -> |
|
|
|
end |
|
|
|
end, [], GroupedCards). |
|
|
|
|
|
|
|
find_sequences(GroupedCards) -> |
|
|
|
Values = lists:sort(maps:keys(GroupedCards)), |
|
|
|
find_consecutive_sequences(Values, GroupedCards, 5). |
|
|
|
|
|
|
|
find_bombs(GroupedCards) -> |
|
|
|
maps:fold(fun(Value, Cards, Acc) -> |
|
|
|
case length(Cards) >= 4 of |
|
|
|
true -> [{Value, Cards}|Acc]; |
|
|
|
true -> [{Value, lists:sublist(Cards, 4)}|Acc]; |
|
|
|
false -> Acc |
|
|
|
end |
|
|
|
end, [], GroupedCards). |
|
|
|
|
|
|
|
find_rockets(GroupedCards) -> |
|
|
|
case {maps:get(?CARD_JOKER_SMALL, GroupedCards, []), |
|
|
|
maps:get(?CARD_JOKER_BIG, GroupedCards, [])} of |
|
|
|
{[Small], [Big]} -> [{?CARD_JOKER_BIG, [Small, Big]}]; |
|
|
|
SmallJoker = maps:get(?CARD_VALUE_SMALL_JOKER, GroupedCards, []), |
|
|
|
BigJoker = maps:get(?CARD_VALUE_BIG_JOKER, GroupedCards, []), |
|
|
|
case {SmallJoker, BigJoker} of |
|
|
|
{[S|_], [B|_]} -> [{rocket, [S, B]}]; |
|
|
|
_ -> [] |
|
|
|
end. |
|
|
|
|
|
|
|
find_consecutive_sequences(Values, GroupedCards, MinLength) -> |
|
|
|
find_sequences_of_length(Values, GroupedCards, MinLength, []). |
|
|
|
find_sequences(GroupedCards) -> |
|
|
|
Values = lists:sort(maps:keys(GroupedCards)), |
|
|
|
find_straight_sequences(Values, GroupedCards, 5, []). |
|
|
|
|
|
|
|
find_straight_sequences([], _, _, Acc) -> Acc; |
|
|
|
find_straight_sequences([H|T], GroupedCards, MinLen, Acc) -> |
|
|
|
case find_sequence_from(H, T, GroupedCards, MinLen) of |
|
|
|
{ok, Seq, Rest} -> |
|
|
|
Cards = [hd(maps:get(V, GroupedCards)) || V <- Seq], |
|
|
|
find_straight_sequences(Rest, GroupedCards, MinLen, [{H, Cards}|Acc]); |
|
|
|
{error, Rest} -> |
|
|
|
find_straight_sequences(Rest, GroupedCards, MinLen, Acc) |
|
|
|
end. |
|
|
|
|
|
|
|
find_sequences_of_length([], _, _, Acc) -> Acc; |
|
|
|
find_sequences_of_length([V|Rest], GroupedCards, MinLength, Acc) -> |
|
|
|
case find_sequence_starting_at(V, Rest, GroupedCards, MinLength) of |
|
|
|
{ok, Sequence} -> |
|
|
|
find_sequences_of_length(Rest, GroupedCards, MinLength, |
|
|
|
[Sequence|Acc]); |
|
|
|
find_sequence_from(Start, Values, GroupedCards, MinLen) -> |
|
|
|
find_sequence_from(Start, Values, GroupedCards, MinLen, [Start], 1). |
|
|
|
|
|
|
|
find_sequence_from(_, [], _, MinLen, Seq, Len) when Len >= MinLen -> |
|
|
|
{ok, lists:reverse(Seq), []}; |
|
|
|
find_sequence_from(_, [], _, _, _, _) -> |
|
|
|
{error, []}; |
|
|
|
find_sequence_from(Prev, [H|T], GroupedCards, MinLen, Seq, Len) -> |
|
|
|
case H =:= Prev + 1 of |
|
|
|
true -> |
|
|
|
find_sequence_from(H, T, GroupedCards, MinLen, [H|Seq], Len+1); |
|
|
|
false when Len >= MinLen -> |
|
|
|
{ok, lists:reverse(Seq), [H|T]}; |
|
|
|
false -> |
|
|
|
find_sequences_of_length(Rest, GroupedCards, MinLength, Acc) |
|
|
|
{error, [H|T]} |
|
|
|
end. |
|
|
|
|
|
|
|
find_sequence_starting_at(Start, Rest, GroupedCards, MinLength) -> |
|
|
|
case collect_sequence(Start, Rest, GroupedCards, []) of |
|
|
|
Seq when length(Seq) >= MinLength -> |
|
|
|
{ok, {Start, Seq}}; |
|
|
|
_ -> |
|
|
|
false |
|
|
|
%% 查找最佳首出组合 |
|
|
|
find_best_lead_combination(HandValue) -> |
|
|
|
% 优先级:顺子 > 连对 > 三带一 > 对子 > 单牌 |
|
|
|
case HandValue#hand_value.sequences of |
|
|
|
[{_, Cards}|_] -> {ok, Cards}; |
|
|
|
[] -> |
|
|
|
case find_straight_pairs(HandValue) of |
|
|
|
{ok, Cards} -> {ok, Cards}; |
|
|
|
none -> |
|
|
|
case find_triple_with_single(HandValue) of |
|
|
|
{ok, Cards} -> {ok, Cards}; |
|
|
|
none -> |
|
|
|
case HandValue#hand_value.pairs of |
|
|
|
[{_, Cards}|_] -> {ok, Cards}; |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.singles of |
|
|
|
[{_, Card}|_] -> {ok, [Card]}; |
|
|
|
[] -> none |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end. |
|
|
|
|
|
|
|
collect_sequence(Current, [Next|Rest], GroupedCards, Acc) when Next =:= Current + 1 -> |
|
|
|
case maps:get(Current, GroupedCards) of |
|
|
|
Cards when length(Cards) >= 1 -> |
|
|
|
collect_sequence(Next, Rest, GroupedCards, [hd(Cards)|Acc]); |
|
|
|
_ -> |
|
|
|
lists:reverse(Acc) |
|
|
|
end; |
|
|
|
collect_sequence(Current, _, GroupedCards, Acc) -> |
|
|
|
case maps:get(Current, GroupedCards) of |
|
|
|
Cards when length(Cards) >= 1 -> |
|
|
|
lists:reverse([hd(Cards)|Acc]); |
|
|
|
_ -> |
|
|
|
lists:reverse(Acc) |
|
|
|
end. |
|
|
|
|
|
|
|
calculate_base_score({Type, Value, _Cards}) -> |
|
|
|
BaseScore = Value * 10, |
|
|
|
TypeMultiplier = case Type of |
|
|
|
?CARD_TYPE_ROCKET -> 100; |
|
|
|
?CARD_TYPE_BOMB -> 80; |
|
|
|
?CARD_TYPE_STRAIGHT -> 40; |
|
|
|
?CARD_TYPE_STRAIGHT_PAIR -> 35; |
|
|
|
?CARD_TYPE_PLANE -> 30; |
|
|
|
?CARD_TYPE_THREE_TWO -> 25; |
|
|
|
?CARD_TYPE_THREE_ONE -> 20; |
|
|
|
?CARD_TYPE_THREE -> 15; |
|
|
|
?CARD_TYPE_PAIR -> 10; |
|
|
|
?CARD_TYPE_SINGLE -> 5 |
|
|
|
%% 查找连对 |
|
|
|
find_straight_pairs(HandValue) -> |
|
|
|
Pairs = HandValue#hand_value.pairs, |
|
|
|
case length(Pairs) >= 3 of |
|
|
|
true -> |
|
|
|
SortedPairs = lists:sort(fun({V1, _}, {V2, _}) -> V1 =< V2 end, Pairs), |
|
|
|
find_consecutive_pairs(SortedPairs, 3); |
|
|
|
false -> none |
|
|
|
end. |
|
|
|
|
|
|
|
find_consecutive_pairs(Pairs, MinLen) -> |
|
|
|
find_consecutive_pairs(Pairs, MinLen, []). |
|
|
|
|
|
|
|
find_consecutive_pairs([], _, []) -> none; |
|
|
|
find_consecutive_pairs([], MinLen, Acc) when length(Acc) >= MinLen -> |
|
|
|
{ok, lists:flatten([Cards || {_, Cards} <- lists:reverse(Acc)])}; |
|
|
|
find_consecutive_pairs([], _, _) -> none; |
|
|
|
find_consecutive_pairs([{V, Cards}|Rest], MinLen, []) -> |
|
|
|
find_consecutive_pairs(Rest, MinLen, [{V, Cards}]); |
|
|
|
find_consecutive_pairs([{V, Cards}|Rest], MinLen, [{PrevV, _}|_]=Acc) -> |
|
|
|
case V =:= PrevV + 1 of |
|
|
|
true -> find_consecutive_pairs(Rest, MinLen, [{V, Cards}|Acc]); |
|
|
|
false when length(Acc) >= MinLen -> |
|
|
|
{ok, lists:flatten([C || {_, C} <- lists:reverse(Acc)])}; |
|
|
|
false -> find_consecutive_pairs(Rest, MinLen, [{V, Cards}]) |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找三带一 |
|
|
|
find_triple_with_single(HandValue) -> |
|
|
|
case {HandValue#hand_value.triples, HandValue#hand_value.singles} of |
|
|
|
{[{TripleV, TripleCards}|_], [{SingleV, SingleCard}|_]} when TripleV =/= SingleV -> |
|
|
|
{ok, TripleCards ++ [SingleCard]}; |
|
|
|
_ -> none |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找单牌 |
|
|
|
find_single_card(HandValue) -> |
|
|
|
case HandValue#hand_value.singles of |
|
|
|
[{_, Card}|_] -> [Card]; |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.pairs of |
|
|
|
[{_, [Card|_]}|_] -> [Card]; |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.triples of |
|
|
|
[{_, [Card|_]}|_] -> [Card]; |
|
|
|
[] -> [] |
|
|
|
end |
|
|
|
end |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找最小单牌 |
|
|
|
find_smallest_single(HandValue) -> |
|
|
|
AllSingles = case HandValue#hand_value.singles of |
|
|
|
[] -> []; |
|
|
|
Singles -> [{V, C} || {V, C} <- Singles] |
|
|
|
end, |
|
|
|
BaseScore * TypeMultiplier / 100. |
|
|
|
|
|
|
|
case AllSingles of |
|
|
|
[] -> []; |
|
|
|
_ -> |
|
|
|
[{_, Card}|_] = lists:sort(fun({V1, _}, {V2, _}) -> V1 =< V2 end, AllSingles), |
|
|
|
[Card] |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找最小炸弹 |
|
|
|
find_smallest_bomb(HandValue) -> |
|
|
|
case HandValue#hand_value.bombs of |
|
|
|
[] -> []; |
|
|
|
Bombs -> |
|
|
|
[{_, Cards}|_] = lists:sort(fun({V1, _}, {V2, _}) -> V1 =< V2 end, Bombs), |
|
|
|
Cards |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找任意可出牌 |
|
|
|
find_any_playable(HandValue) -> |
|
|
|
case find_single_card(HandValue) of |
|
|
|
[] -> []; |
|
|
|
Cards -> Cards |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找安全首出 |
|
|
|
find_safe_lead(HandValue) -> |
|
|
|
% 优先出对子和三张 |
|
|
|
case HandValue#hand_value.pairs of |
|
|
|
[{_, Cards}|_] -> {ok, Cards}; |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.triples of |
|
|
|
[{_, Cards}|_] -> {ok, Cards}; |
|
|
|
[] -> none |
|
|
|
end |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找安全跟牌 |
|
|
|
find_safe_follow(HandValue, LastPlay) -> |
|
|
|
% 简单实现:找到比LastPlay大的最小牌 |
|
|
|
find_minimum_greater_combination(HandValue, LastPlay). |
|
|
|
|
|
|
|
%% 查找控制组合 |
|
|
|
find_control_combination(HandValue) -> |
|
|
|
% 优先出单牌,保留炸弹和对子 |
|
|
|
case HandValue#hand_value.singles of |
|
|
|
[{_, Card}|_] -> {ok, [Card]}; |
|
|
|
[] -> find_safe_lead(HandValue) |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找节奏出牌 |
|
|
|
find_tempo_play(HandValue) -> |
|
|
|
% 简单实现:出最小的单牌 |
|
|
|
[Card] = find_smallest_single(HandValue), |
|
|
|
[Card]. |
|
|
|
|
|
|
|
%% 查找最小的大于LastPlay的组合 |
|
|
|
find_minimum_greater_combination(HandValue, LastPlay) -> |
|
|
|
% 简单实现,实际应该根据LastPlay的牌型查找对应的更大牌型 |
|
|
|
% 这里假设LastPlay是单牌 |
|
|
|
case LastPlay of |
|
|
|
[LastCard] -> |
|
|
|
{LastValue, _} = LastCard, |
|
|
|
GreaterSingles = [{V, C} || {V, C} <- HandValue#hand_value.singles, V > LastValue], |
|
|
|
case GreaterSingles of |
|
|
|
[] -> none; |
|
|
|
_ -> |
|
|
|
[{_, Card}|_] = lists:sort(fun({V1, _}, {V2, _}) -> V1 =< V2 end, GreaterSingles), |
|
|
|
{ok, [Card]} |
|
|
|
end; |
|
|
|
_ -> none % 简化处理,实际应该处理各种牌型 |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找压制性出牌 |
|
|
|
find_crushing_play(HandValue, _LastPlay) -> |
|
|
|
% 优先使用炸弹 |
|
|
|
case HandValue#hand_value.bombs of |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.rockets of |
|
|
|
[] -> none; |
|
|
|
[{_, Cards}|_] -> {ok, Cards} |
|
|
|
end; |
|
|
|
[{_, Cards}|_] -> {ok, Cards} |
|
|
|
end. |
|
|
|
|
|
|
|
%% 查找最强组合 |
|
|
|
find_strongest_combination(HandValue) -> |
|
|
|
% 优先级:火箭 > 炸弹 > 顺子 > 三带 > 对子 > 单牌 |
|
|
|
case HandValue#hand_value.rockets of |
|
|
|
[{_, Cards}|_] -> {ok, Cards}; |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.bombs of |
|
|
|
[{_, Cards}|_] -> {ok, Cards}; |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.sequences of |
|
|
|
[{_, Cards}|_] -> {ok, Cards}; |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.triples of |
|
|
|
[{_, Cards}|_] -> {ok, Cards}; |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.pairs of |
|
|
|
[{_, Cards}|_] -> {ok, Cards}; |
|
|
|
[] -> |
|
|
|
case HandValue#hand_value.singles of |
|
|
|
[{_, Card}|_] -> {ok, [Card]}; |
|
|
|
[] -> none |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end. |
|
|
|
|
|
|
|
%% 判断是否应该使用炸弹 |
|
|
|
should_use_bomb(HandValue, LastPlay) -> |
|
|
|
% 简单实现:当剩余牌数少于8张且有炸弹时考虑使用 |
|
|
|
length(lists:flatten([C || {_, C} <- HandValue#hand_value.singles] ++ |
|
|
|
[C || {_, C} <- HandValue#hand_value.pairs] ++ |
|
|
|
[C || {_, C} <- HandValue#hand_value.triples])) < 8 andalso |
|
|
|
HandValue#hand_value.bombs =/= []. |
|
|
|
|
|
|
|
%% 判断是否应该保持控制 |
|
|
|
should_maintain_control(HandValue, LastPlay) -> |
|
|
|
% 简单实现:当有足够的高牌时尝试保持控制 |
|
|
|
HighCards = [{V, C} || {V, C} <- HandValue#hand_value.singles, V >= ?CARD_VALUE_J], |
|
|
|
length(HighCards) >= 2. |
|
|
|
|
|
|
|
%% 计算基础得分 |
|
|
|
calculate_base_score(Move) -> |
|
|
|
% 简单实现:根据牌型和牌值计算基础得分 |
|
|
|
case length(Move) of |
|
|
|
1 -> 0.3; |
|
|
|
2 -> |
|
|
|
[{V1, _}, {V2, _}] = Move, |
|
|
|
case V1 =:= V2 of |
|
|
|
true -> 0.5; % 对子 |
|
|
|
false -> 0.4 % 其他两张牌 |
|
|
|
end; |
|
|
|
3 -> 0.6; |
|
|
|
4 -> |
|
|
|
[{V1, _}, {V2, _}, {V3, _}, {V4, _}] = Move, |
|
|
|
case V1 =:= V2 andalso V2 =:= V3 andalso V3 =:= V4 of |
|
|
|
true -> 0.9; % 炸弹 |
|
|
|
false -> 0.7 % 其他四张牌 |
|
|
|
end; |
|
|
|
_ -> 0.8 % 长牌型 |
|
|
|
end. |
|
|
|
|
|
|
|
%% 计算节奏得分 |
|
|
|
calculate_tempo_score(Move, Context) -> |
|
|
|
% 简单实现:根据游戏阶段和出牌类型计算节奏得分 |
|
|
|
case Context#strategy_context.game_stage of |
|
|
|
early_game -> calculate_early_tempo(Move, Context); |
|
|
|
mid_game -> calculate_mid_tempo(Move, Context); |
|
|
|
end_game -> calculate_end_tempo(Move, Context) |
|
|
|
early_game -> 0.4; |
|
|
|
mid_game -> 0.6; |
|
|
|
end_game -> 0.8 |
|
|
|
end. |
|
|
|
|
|
|
|
%% 计算控制得分 |
|
|
|
calculate_control_score(Move, Context) -> |
|
|
|
BaseControl = case Move of |
|
|
|
{Type, _, _} when Type =:= ?CARD_TYPE_BOMB; |
|
|
|
Type =:= ?CARD_TYPE_ROCKET -> 1.0; |
|
|
|
{_, Value, _} when Value >= ?CARD_2 -> 0.8; |
|
|
|
_ -> 0.5 |
|
|
|
end, |
|
|
|
BaseControl * Context#strategy_context.control_level. |
|
|
|
% 简单实现:根据控制水平计算控制得分 |
|
|
|
Context#strategy_context.control_level. |
|
|
|
|
|
|
|
%% 计算效率得分 |
|
|
|
calculate_efficiency_score(Move, Context) -> |
|
|
|
{Type, _, Cards} = Move, |
|
|
|
CardsUsed = length(Cards), |
|
|
|
RemainingCards = Context#strategy_context.cards_remaining - CardsUsed, |
|
|
|
Efficiency = CardsUsed / max(1, Context#strategy_context.cards_remaining), |
|
|
|
Efficiency * (1 + (20 - RemainingCards) / 20). |
|
|
|
% 简单实现:根据出牌数量和剩余牌数计算效率得分 |
|
|
|
CardsPlayed = length(Move), |
|
|
|
CardsRemaining = Context#strategy_context.cards_remaining, |
|
|
|
min(1.0, CardsPlayed / max(1, CardsRemaining) * 3). |
|
|
|
|
|
|
|
%% 根据上下文调整得分 |
|
|
|
adjust_score_for_context(Score, Move, Context, LastPlay) -> |
|
|
|
case {Context#strategy_context.role, LastPlay} of |
|
|
|
{dizhu, none} -> Score * 1.2; |
|
|
|
{dizhu, _} -> Score * 1.1; |
|
|
|
{nongmin, _} -> Score |
|
|
|
end. |
|
|
|
|
|
|
|
calculate_early_tempo(Move, Context) -> |
|
|
|
case Move of |
|
|
|
{Type, _, _} when Type =:= ?CARD_TYPE_SINGLE; |
|
|
|
Type =:= ?CARD_TYPE_PAIR -> |
|
|
|
0.7; |
|
|
|
{Type, _, _} when Type =:= ?CARD_TYPE_STRAIGHT; |
|
|
|
Type =:= ?CARD_TYPE_STRAIGHT_PAIR -> |
|
|
|
0.9; |
|
|
|
_ -> |
|
|
|
0.5 |
|
|
|
end. |
|
|
|
|
|
|
|
calculate_mid_tempo(Move, Context) -> |
|
|
|
case Move of |
|
|
|
{Type, _, _} when Type =:= ?CARD_TYPE_THREE_ONE; |
|
|
|
Type =:= ?CARD_TYPE_THREE_TWO -> |
|
|
|
0.8; |
|
|
|
{Type, _, _} when Type =:= ?CARD_TYPE_PLANE -> |
|
|
|
0.9; |
|
|
|
_ -> |
|
|
|
0.6 |
|
|
|
end. |
|
|
|
|
|
|
|
calculate_end_tempo(Move, Context) -> |
|
|
|
case Move of |
|
|
|
{Type, _, _} when Type =:= ?CARD_TYPE_BOMB; |
|
|
|
Type =:= ?CARD_TYPE_ROCKET -> |
|
|
|
1.0; |
|
|
|
{_, Value, _} when Value >= ?CARD_2 -> |
|
|
|
0.9; |
|
|
|
_ -> |
|
|
|
0.7 |
|
|
|
% 简单实现:根据游戏阶段和角色调整得分 |
|
|
|
AdjustedScore = case Context#strategy_context.role of |
|
|
|
dizhu -> Score * 1.1; |
|
|
|
nongmin -> Score * 0.9 |
|
|
|
end, |
|
|
|
|
|
|
|
% 根据游戏阶段进一步调整 |
|
|
|
case Context#strategy_context.game_stage of |
|
|
|
early_game -> AdjustedScore * 0.9; |
|
|
|
mid_game -> AdjustedScore; |
|
|
|
end_game -> AdjustedScore * 1.1 |
|
|
|
end. |