Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 

301 Zeilen
8.9 KiB

-module(game_core).
-export([start_game/3, play_cards/3, get_game_state/1, is_valid_play/2]).
-export([init_deck/0, deal_cards/1, get_card_type/1, compare_cards/2]).
-record(game_state, {
players = [], % [{Pid, Cards, Role}]
current_player, % Pid
last_play = [], % {Pid, Cards}
played_cards = [], % [{Pid, Cards}]
stage = waiting, % waiting | playing | finished
landlord_cards = [] % 地主牌
}).
%% 初始化扑克牌
init_deck() ->
Colors = ["", "", "", ""],
Numbers = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"],
Cards = [{Color, Number} || Color <- Colors, Number <- Numbers],
Jokers = [{"", "小王"}, {"", "大王"}],
shuffle(Cards ++ Jokers).
%% 发牌
deal_cards(Deck) ->
{Players, Landlord} = lists:split(51, Deck),
{lists:sublist(Players, 1, 17),
lists:sublist(Players, 18, 17),
lists:sublist(Players, 35, 17),
Landlord}.
%% 开始游戏
start_game(Player1, Player2, Player3) ->
Deck = init_deck(),
{Cards1, Cards2, Cards3, LandlordCards} = deal_cards(Deck),
% 随机选择地主
LandlordIdx = rand:uniform(3),
Players = assign_roles([{Player1, Cards1}, {Player2, Cards2}, {Player3, Cards3}], LandlordIdx),
#game_state{
players = Players,
current_player = element(1, lists:nth(LandlordIdx, Players)),
landlord_cards = LandlordCards
}.
%% 出牌
play_cards(GameState, PlayerPid, Cards) ->
case validate_play(GameState, PlayerPid, Cards) of
true ->
NewState = update_game_state(GameState, PlayerPid, Cards),
check_game_end(NewState);
false ->
{error, invalid_play}
end.
%% 验证出牌
validate_play(GameState, PlayerPid, Cards) ->
case get_player_cards(GameState, PlayerPid) of
{ok, PlayerCards} ->
has_cards(PlayerCards, Cards) andalso
is_valid_play(Cards, GameState#game_state.last_play);
_ ->
false
end.
%% 判断牌型
get_card_type(Cards) ->
Sorted = sort_cards(Cards),
case analyze_pattern(Sorted) of
{Type, Value} -> {ok, Type, Value};
invalid -> {error, invalid_pattern}
end.
%% 内部函数
shuffle(List) ->
[X || {_, X} <- lists:sort([{rand:uniform(), N} || N <- List])].
analyze_pattern(Cards) ->
case length(Cards) of
1 -> {single, card_value(hd(Cards))};
2 -> analyze_pair(Cards);
3 -> analyze_triple(Cards);
4 -> analyze_four_cards(Cards);
_ -> analyze_complex_pattern(Cards)
end.
analyze_pair([{_, N}, {_, N}]) -> {pair, card_value({any, N})};
analyze_pair(_) -> invalid.
analyze_triple([{_, N}, {_, N}, {_, N}]) -> {triple, card_value({any, N})};
analyze_triple(_) -> invalid.
analyze_four_cards(Cards) ->
case group_cards(Cards) of
#{4 := [Value]} -> {bomb, card_value({any, Value})};
_ -> analyze_four_with_two(Cards)
end.
analyze_complex_pattern(Cards) ->
case is_straight(Cards) of
true -> {straight, highest_card_value(Cards)};
false -> analyze_other_patterns(Cards)
end.
%% 获取游戏状态
get_game_state(GameId) ->
% 简单实现,实际应该从某种存储中获取
{ok, #game_state{}}.
%% 初始化游戏状态
init_game_state() ->
% 创建一个空的游戏状态
#game_state{
players = [],
current_player = undefined,
last_play = [],
played_cards = [],
stage = waiting,
landlord_cards = []
}.
%% 判断出牌是否有效
is_valid_play(Cards, LastPlay) ->
% 如果是第一手牌,任何合法牌型都可以
case LastPlay of
[] -> is_valid_card_type(Cards);
{_, LastCards} ->
% 检查牌型是否相同且大小是否更大
{ok, Type1, Value1} = get_card_type(Cards),
{ok, Type2, Value2} = get_card_type(LastCards),
(Type1 =:= Type2 andalso Value1 > Value2) orelse
(Type1 =:= bomb andalso Type2 =/= bomb)
end.
%% 判断牌型是否合法
is_valid_card_type(Cards) ->
case get_card_type(Cards) of
{ok, _, _} -> true;
_ -> false
end.
%% 比较两手牌的大小
compare_cards(Cards1, Cards2) ->
{ok, Type1, Value1} = get_card_type(Cards1),
{ok, Type2, Value2} = get_card_type(Cards2),
if
Type1 =:= bomb andalso Type2 =/= bomb -> greater;
Type2 =:= bomb andalso Type1 =/= bomb -> lesser;
Type1 =:= Type2 andalso Value1 > Value2 -> greater;
Type1 =:= Type2 andalso Value1 < Value2 -> lesser;
true -> incomparable
end.
%% 分配角色
assign_roles(PlayerCards, LandlordIdx) ->
lists:map(
fun({Idx, {Pid, Cards}}) ->
Role = case Idx of
LandlordIdx -> landlord;
_ -> farmer
end,
{Pid, Cards, Role}
end,
lists:zip(lists:seq(1, length(PlayerCards)), PlayerCards)
).
%% 更新游戏状态
update_game_state(GameState, PlayerPid, Cards) ->
% 更新玩家手牌
UpdatedPlayers = lists:map(
fun({Pid, PlayerCards, Role}) when Pid =:= PlayerPid ->
{Pid, PlayerCards -- Cards, Role};
(Player) -> Player
end,
GameState#game_state.players
),
% 更新出牌记录和当前玩家
NextPlayer = get_next_player(GameState, PlayerPid),
GameState#game_state{
players = UpdatedPlayers,
current_player = NextPlayer,
last_play = {PlayerPid, Cards},
played_cards = [{PlayerPid, Cards} | GameState#game_state.played_cards]
}.
%% 获取下一个玩家
get_next_player(GameState, CurrentPid) ->
Players = GameState#game_state.players,
PlayerPids = [Pid || {Pid, _, _} <- Players],
CurrentIdx = get_player_index(PlayerPids, CurrentPid),
lists:nth(1 + (CurrentIdx rem length(PlayerPids)), PlayerPids).
%% 获取玩家索引
get_player_index(PlayerPids, TargetPid) ->
{Idx, _} = lists:foldl(
fun(Pid, {Idx, Found}) ->
case Pid =:= TargetPid of
true -> {Idx, Idx};
false -> {Idx + 1, Found}
end
end,
{1, not_found},
PlayerPids
),
Idx.
%% 检查游戏是否结束
check_game_end(GameState) ->
case lists:any(
fun({_, Cards, _}) -> length(Cards) =:= 0 end,
GameState#game_state.players
) of
true ->
Winner = lists:keyfind(0, 2, GameState#game_state.players),
{game_over, Winner, GameState#game_state{stage = finished}};
false ->
{continue, GameState}
end.
%% 获取玩家手牌
get_player_cards(GameState, PlayerPid) ->
case lists:keyfind(PlayerPid, 1, GameState#game_state.players) of
{_, Cards, _} -> {ok, Cards};
false -> {error, player_not_found}
end.
%% 检查玩家是否有这些牌
has_cards(PlayerCards, CardsToPlay) ->
lists:all(
fun(Card) ->
Count = count_card(PlayerCards, Card),
CountToPlay = count_card(CardsToPlay, Card),
Count >= CountToPlay
end,
lists:usort(CardsToPlay)
).
%% 计算特定牌的数量
count_card(Cards, TargetCard) ->
length([Card || Card <- Cards, Card =:= TargetCard]).
%% 分析四带二
analyze_four_with_two(Cards) ->
Grouped = group_cards(Cards),
case maps:get(4, Grouped, []) of
[Value] ->
case length(Cards) of
6 -> {four_with_two, card_value({any, Value})};
_ -> invalid
end;
_ -> invalid
end.
%% 分组牌
group_cards(Cards) ->
lists:foldl(
fun({_, Number}, Acc) ->
Count = maps:get(Number, Acc, 0) + 1,
Acc#{Number => Count}
end,
#{},
Cards
).
%% 获取最高牌值
highest_card_value(Cards) ->
lists:max([card_value(Card) || Card <- Cards]).
%% 牌值转换
card_value({_, "3"}) -> 3;
card_value({_, "4"}) -> 4;
card_value({_, "5"}) -> 5;
card_value({_, "6"}) -> 6;
card_value({_, "7"}) -> 7;
card_value({_, "8"}) -> 8;
card_value({_, "9"}) -> 9;
card_value({_, "10"}) -> 10;
card_value({_, "J"}) -> 11;
card_value({_, "Q"}) -> 12;
card_value({_, "K"}) -> 13;
card_value({_, "A"}) -> 14;
card_value({_, "2"}) -> 15;
card_value({_, "小王"}) -> 16;
card_value({_, "大王"}) -> 17;
card_value({any, Value}) -> card_value({"", Value}).
%% 检查是否为顺子
is_straight(Cards) ->
Values = [card_value(Card) || Card <- Cards],
SortedValues = lists:sort(Values),
length(SortedValues) >= 5 andalso
lists:max(SortedValues) =< 14 andalso % A以下的牌才能组成顺子
SortedValues =:= lists:seq(hd(SortedValues), lists:last(SortedValues)).
%% 分析其他牌型
analyze_other_patterns(Cards) ->
% 实现其他复杂牌型的分析,如飞机、连对等
% 简单实现,实际应该有更复杂的逻辑
invalid.
%% 排序牌
sort_cards(Cards) ->
lists:sort(fun(A, B) -> card_value(A) =< card_value(B) end, Cards).