|
|
@ -263,7 +263,19 @@ generate_leading_plays(Cards) -> |
|
|
|
|
|
|
|
%% 生成跟牌选择 |
|
|
|
generate_following_plays(Cards, {Type, Value, _} = LastPlay) -> |
|
|
|
ValidPlays = find_greater_plays(Cards, Type, Value), |
|
|
|
Components = analyze_components(Cards), |
|
|
|
ValidPlays = case Type of |
|
|
|
?CARD_TYPE_SINGLE -> find_greater_singles(Components, Value); |
|
|
|
?CARD_TYPE_PAIR -> find_greater_pairs(Components, Value); |
|
|
|
?CARD_TYPE_THREE -> find_greater_triples(Components, Value); |
|
|
|
?CARD_TYPE_THREE_ONE -> find_greater_three_one(Components, Value); |
|
|
|
?CARD_TYPE_THREE_TWO -> find_greater_three_two(Components, Value); |
|
|
|
?CARD_TYPE_STRAIGHT -> find_greater_straight(Components, Value); |
|
|
|
?CARD_TYPE_STRAIGHT_PAIR -> find_greater_straight_pair(Components, Value); |
|
|
|
?CARD_TYPE_PLANE -> find_greater_plane(Components, Value); |
|
|
|
?CARD_TYPE_BOMB -> find_greater_bomb(Components, Value); |
|
|
|
_ -> [] |
|
|
|
end, |
|
|
|
BombPlays = find_bomb_plays(Cards), |
|
|
|
RocketPlay = find_rocket_play(Cards), |
|
|
|
|
|
|
@ -440,6 +452,120 @@ generate_three_two(Triples, Pairs) -> |
|
|
|
{PairValue, PairCards} <- Pairs, |
|
|
|
PairValue =/= TripleValue]. |
|
|
|
|
|
|
|
%% 评估出牌潜力 |
|
|
|
score_play_potential(Play, LastPlay) -> |
|
|
|
{Type, Value, Cards} = Play, |
|
|
|
BaseScore = case Type of |
|
|
|
?CARD_TYPE_ROCKET -> 1.0; |
|
|
|
?CARD_TYPE_BOMB -> 0.9; |
|
|
|
?CARD_TYPE_STRAIGHT -> 0.8; |
|
|
|
?CARD_TYPE_STRAIGHT_PAIR -> 0.75; |
|
|
|
?CARD_TYPE_PLANE -> 0.7; |
|
|
|
?CARD_TYPE_THREE_TWO -> 0.6; |
|
|
|
?CARD_TYPE_THREE_ONE -> 0.5; |
|
|
|
?CARD_TYPE_THREE -> 0.4; |
|
|
|
?CARD_TYPE_PAIR -> 0.3; |
|
|
|
?CARD_TYPE_SINGLE -> 0.2 |
|
|
|
end, |
|
|
|
|
|
|
|
% 根据牌值调整分数 |
|
|
|
ValueFactor = Value / ?CARD_VALUE_BIG_JOKER, |
|
|
|
|
|
|
|
% 根据出牌数量调整分数 |
|
|
|
CountFactor = length(Cards) / 10, |
|
|
|
|
|
|
|
% 计算最终分数 |
|
|
|
BaseScore * (1 + ValueFactor) * (1 + CountFactor). |
|
|
|
|
|
|
|
%% 内部辅助函数 |
|
|
|
|
|
|
|
%% 按值分组牌 |
|
|
|
group_cards_by_value(Cards) -> |
|
|
|
lists:foldl(fun(Card, Acc) -> |
|
|
|
{Value, _} = Card, |
|
|
|
maps:update_with(Value, fun(List) -> [Card | List] end, [Card], Acc) |
|
|
|
end, #{}, Cards). |
|
|
|
|
|
|
|
%% 查找顺子 |
|
|
|
find_sequences(GroupedCards) -> |
|
|
|
Values = lists:sort(maps:keys(GroupedCards)), |
|
|
|
find_sequences(Values, GroupedCards, []). |
|
|
|
|
|
|
|
find_sequences([], _, Acc) -> Acc; |
|
|
|
find_sequences([V | Rest], GroupedCards, Acc) -> |
|
|
|
case find_sequence_starting_at(V, Rest, GroupedCards) of |
|
|
|
{Seq, NewRest} when length(Seq) >= 5 -> |
|
|
|
Cards = lists:flatten([maps:get(Value, GroupedCards) || Value <- Seq]), |
|
|
|
find_sequences(NewRest, GroupedCards, [{V, Cards} | Acc]); |
|
|
|
{_, NewRest} -> |
|
|
|
find_sequences(NewRest, GroupedCards, Acc) |
|
|
|
end. |
|
|
|
|
|
|
|
find_sequence_starting_at(Start, Rest, _) when Start >= ?CARD_VALUE_2 -> |
|
|
|
{[], Rest}; % 2及以上不能组成顺子 |
|
|
|
find_sequence_starting_at(Start, Rest, GroupedCards) -> |
|
|
|
find_sequence_starting_at(Start, Rest, GroupedCards, [Start]). |
|
|
|
|
|
|
|
find_sequence_starting_at(_, [], _, Acc) -> |
|
|
|
{lists:reverse(Acc), []}; |
|
|
|
find_sequence_starting_at(Prev, [Next | Rest], GroupedCards, Acc) -> |
|
|
|
case Next of |
|
|
|
Next when Next =:= Prev + 1, Next < ?CARD_VALUE_2 -> |
|
|
|
find_sequence_starting_at(Next, Rest, GroupedCards, [Next | Acc]); |
|
|
|
_ -> |
|
|
|
{lists:reverse(Acc), [Next | Rest]} |
|
|
|
end. |
|
|
|
|
|
|
|
%% 过滤候选出牌 |
|
|
|
filter_candidates(Plays, Context) -> |
|
|
|
% 根据上下文过滤出牌 |
|
|
|
% 例如:在结束阶段可能会优先选择能一次出完的牌 |
|
|
|
case Context#play_context.game_stage of |
|
|
|
end_game when Context#play_context.cards_remaining =< 4 -> |
|
|
|
% 在结束阶段且剩余牌数少时,优先考虑能一次出完的牌 |
|
|
|
[Play || Play = {_, _, Cards} <- Plays, length(Cards) =:= Context#play_context.cards_remaining]; |
|
|
|
_ -> |
|
|
|
Plays |
|
|
|
end. |
|
|
|
|
|
|
|
%% 获取上一手牌 |
|
|
|
get_last_play(GameState) -> |
|
|
|
% 从游戏状态中获取上一手牌 |
|
|
|
% 简化实现,实际需要根据具体游戏状态获取 |
|
|
|
maps:get(last_play, GameState, none). |
|
|
|
|
|
|
|
%% 计算比上家大的牌数量 |
|
|
|
count_greater_cards(Cards, LastPlay) -> |
|
|
|
case LastPlay of |
|
|
|
none -> length(Cards); |
|
|
|
{Type, Value, _} -> |
|
|
|
Components = analyze_components(Cards), |
|
|
|
length(find_greater_plays(Cards, Type, Value)) |
|
|
|
end. |
|
|
|
|
|
|
|
%% 评估位置 |
|
|
|
evaluate_position(AIState) -> |
|
|
|
% 评估当前位置的价值 |
|
|
|
% 简化实现,实际需要考虑更多因素 |
|
|
|
case AIState#ai_state.role of |
|
|
|
dizhu -> 0.7; |
|
|
|
nongmin -> 0.5 |
|
|
|
end. |
|
|
|
|
|
|
|
%% 评估控制力 |
|
|
|
evaluate_control(AIState) -> |
|
|
|
% 评估控制力 |
|
|
|
% 简化实现,实际需要考虑更多因素 |
|
|
|
ControlCards = count_control_cards(AIState#ai_state.hand_cards), |
|
|
|
TotalCards = length(AIState#ai_state.hand_cards), |
|
|
|
ControlCards / max(1, TotalCards). |
|
|
|
|
|
|
|
%% 计算剩余总牌数 |
|
|
|
count_total_remaining_cards(GameState) -> |
|
|
|
% 计算游戏中剩余的总牌数 |
|
|
|
% 简化实现,实际需要根据具体游戏状态计算 |
|
|
|
maps:get(total_remaining, GameState, 54). |
|
|
|
|
|
|
|
%% 查找特定牌型 |
|
|
|
find_greater_plays(Cards, Type, MinValue) -> |
|
|
|
Components = analyze_components(Cards), |
|
|
@ -591,69 +717,31 @@ find_consecutive_triple_sequence(Value, _, [{NextValue, NextCards} | Rest], Acc) |
|
|
|
find_consecutive_triple_sequence(_, _, Rest, Acc) -> |
|
|
|
{lists:flatten(lists:reverse(Acc)), Rest}. |
|
|
|
|
|
|
|
%% 计算早期、中期和末期的节奏分数 |
|
|
|
calculate_early_tempo({Type, Value, _}, _Context) -> |
|
|
|
%% 计算早期节奏 |
|
|
|
calculate_early_tempo(Play, Context) -> |
|
|
|
{Type, _, Cards} = Play, |
|
|
|
case Type of |
|
|
|
?CARD_TYPE_SINGLE when Value < ?CARD_VALUE_2 -> 0.8; |
|
|
|
?CARD_TYPE_PAIR when Value < ?CARD_VALUE_2 -> 0.7; |
|
|
|
?CARD_TYPE_STRAIGHT -> 0.9; |
|
|
|
?CARD_TYPE_STRAIGHT_PAIR -> 0.85; |
|
|
|
_ -> 0.5 |
|
|
|
end. |
|
|
|
|
|
|
|
calculate_mid_tempo({Type, Value, _}, _Context) -> |
|
|
|
case Type of |
|
|
|
?CARD_TYPE_THREE_ONE -> 0.8; |
|
|
|
?CARD_TYPE_THREE_TWO -> 0.85; |
|
|
|
?CARD_TYPE_PLANE -> 0.9; |
|
|
|
?CARD_TYPE_BOMB -> 0.7; |
|
|
|
?CARD_TYPE_SINGLE when Context#play_context.cards_remaining > 10 -> 0.3; |
|
|
|
?CARD_TYPE_PAIR when Context#play_context.cards_remaining > 10 -> 0.5; |
|
|
|
?CARD_TYPE_STRAIGHT -> 0.8; |
|
|
|
_ -> 0.6 |
|
|
|
end. |
|
|
|
|
|
|
|
calculate_end_tempo({Type, Value, _}, Context) -> |
|
|
|
CardsLeft = Context#play_context.cards_remaining, |
|
|
|
%% 计算中期节奏 |
|
|
|
calculate_mid_tempo(Play, Context) -> |
|
|
|
{Type, _, Cards} = Play, |
|
|
|
case Type of |
|
|
|
?CARD_TYPE_BOMB -> 0.9; |
|
|
|
?CARD_TYPE_ROCKET -> 1.0; |
|
|
|
_ when CardsLeft =< 4 -> 0.95; |
|
|
|
_ -> 0.7 |
|
|
|
end. |
|
|
|
|
|
|
|
%% 过滤候选出牌 |
|
|
|
filter_candidates(Plays, Context) -> |
|
|
|
case Context#play_context.game_stage of |
|
|
|
early_game -> |
|
|
|
filter_early_game_plays(Plays, Context); |
|
|
|
mid_game -> |
|
|
|
filter_mid_game_plays(Plays, Context); |
|
|
|
end_game -> |
|
|
|
filter_end_game_plays(Plays, Context) |
|
|
|
end. |
|
|
|
|
|
|
|
filter_early_game_plays(Plays, _Context) -> |
|
|
|
% 早期游戏倾向于出小牌,保留炸弹 |
|
|
|
[Play || {Type, Value, _} = Play <- Plays, |
|
|
|
Type =/= ?CARD_TYPE_BOMB orelse Value >= ?CARD_VALUE_2]. |
|
|
|
|
|
|
|
filter_mid_game_plays(Plays, Context) -> |
|
|
|
% 中期游戏根据局势决定是否使用炸弹 |
|
|
|
case Context#play_context.control_factor < 0.5 of |
|
|
|
true -> Plays; |
|
|
|
false -> |
|
|
|
[Play || {Type, _, _} = Play <- Plays, |
|
|
|
Type =/= ?CARD_TYPE_BOMB] |
|
|
|
?CARD_TYPE_SINGLE when Context#play_context.cards_remaining > 5 -> 0.4; |
|
|
|
?CARD_TYPE_PAIR when Context#play_context.cards_remaining > 5 -> 0.6; |
|
|
|
?CARD_TYPE_STRAIGHT -> 0.7; |
|
|
|
_ -> 0.5 |
|
|
|
end. |
|
|
|
|
|
|
|
filter_end_game_plays(Plays, _Context) -> |
|
|
|
% 末期游戏可以使用任何牌型 |
|
|
|
Plays. |
|
|
|
|
|
|
|
%% 对手牌进行分组 |
|
|
|
group_cards_by_value(Cards) -> |
|
|
|
lists:foldl(fun(Card, Acc) -> |
|
|
|
{Value, _} = Card, |
|
|
|
maps:update_with(Value, |
|
|
|
fun(List) -> [Card|List] end, |
|
|
|
[Card], |
|
|
|
Acc) |
|
|
|
end, maps:new(), Cards). |
|
|
|
%% 计算后期节奏 |
|
|
|
calculate_end_tempo(Play, Context) -> |
|
|
|
{_, _, Cards} = Play, |
|
|
|
case length(Cards) of |
|
|
|
L when L =:= Context#play_context.cards_remaining -> 1.0; |
|
|
|
L when L > Context#play_context.cards_remaining / 2 -> 0.8; |
|
|
|
_ -> 0.6 |
|
|
|
end. |