浏览代码

ft:修改调整

master
SisMaker 1 个月前
父节点
当前提交
f44eb9594d
共有 1 个文件被更改,包括 378 次插入106 次删除
  1. +378
    -106
      src/doudizhu_ai_strategy.erl

+ 378
- 106
src/doudizhu_ai_strategy.erl 查看文件

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

正在加载...
取消
保存