From da7dde52e3278907b390367004d81fd2d4a29bce Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Sat, 8 Mar 2025 13:06:26 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ai_core.erl | 208 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 148 insertions(+), 60 deletions(-) diff --git a/src/ai_core.erl b/src/ai_core.erl index eb491e1..8e902d6 100644 --- a/src/ai_core.erl +++ b/src/ai_core.erl @@ -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). \ No newline at end of file +%% 计算后期节奏 +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. \ No newline at end of file