You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

300 line
8.9 KiB

1 月之前
  1. -module(game_core).
  2. -export([start_game/3, play_cards/3, get_game_state/1, is_valid_play/2]).
  3. -export([init_deck/0, deal_cards/1, get_card_type/1, compare_cards/2]).
  4. -record(game_state, {
  5. players = [], % [{Pid, Cards, Role}]
  6. current_player, % Pid
  7. last_play = [], % {Pid, Cards}
  8. played_cards = [], % [{Pid, Cards}]
  9. stage = waiting, % waiting | playing | finished
  10. landlord_cards = [] % 地主牌
  11. }).
  12. %% 初始化扑克牌
  13. init_deck() ->
  14. Colors = ["", "", "", ""],
  15. Numbers = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"],
  16. Cards = [{Color, Number} || Color <- Colors, Number <- Numbers],
  17. Jokers = [{"", "小王"}, {"", "大王"}],
  18. shuffle(Cards ++ Jokers).
  19. %% 发牌
  20. deal_cards(Deck) ->
  21. {Players, Landlord} = lists:split(51, Deck),
  22. {lists:sublist(Players, 1, 17),
  23. lists:sublist(Players, 18, 17),
  24. lists:sublist(Players, 35, 17),
  25. Landlord}.
  26. %% 开始游戏
  27. start_game(Player1, Player2, Player3) ->
  28. Deck = init_deck(),
  29. {Cards1, Cards2, Cards3, LandlordCards} = deal_cards(Deck),
  30. % 随机选择地主
  31. LandlordIdx = rand:uniform(3),
  32. Players = assign_roles([{Player1, Cards1}, {Player2, Cards2}, {Player3, Cards3}], LandlordIdx),
  33. #game_state{
  34. players = Players,
  35. current_player = element(1, lists:nth(LandlordIdx, Players)),
  36. landlord_cards = LandlordCards
  37. }.
  38. %% 出牌
  39. play_cards(GameState, PlayerPid, Cards) ->
  40. case validate_play(GameState, PlayerPid, Cards) of
  41. true ->
  42. NewState = update_game_state(GameState, PlayerPid, Cards),
  43. check_game_end(NewState);
  44. false ->
  45. {error, invalid_play}
  46. end.
  47. %% 验证出牌
  48. validate_play(GameState, PlayerPid, Cards) ->
  49. case get_player_cards(GameState, PlayerPid) of
  50. {ok, PlayerCards} ->
  51. has_cards(PlayerCards, Cards) andalso
  52. is_valid_play(Cards, GameState#game_state.last_play);
  53. _ ->
  54. false
  55. end.
  56. %% 判断牌型
  57. get_card_type(Cards) ->
  58. Sorted = sort_cards(Cards),
  59. case analyze_pattern(Sorted) of
  60. {Type, Value} -> {ok, Type, Value};
  61. invalid -> {error, invalid_pattern}
  62. end.
  63. %% 内部函数
  64. shuffle(List) ->
  65. [X || {_, X} <- lists:sort([{rand:uniform(), N} || N <- List])].
  66. analyze_pattern(Cards) ->
  67. case length(Cards) of
  68. 1 -> {single, card_value(hd(Cards))};
  69. 2 -> analyze_pair(Cards);
  70. 3 -> analyze_triple(Cards);
  71. 4 -> analyze_four_cards(Cards);
  72. _ -> analyze_complex_pattern(Cards)
  73. end.
  74. analyze_pair([{_, N}, {_, N}]) -> {pair, card_value({any, N})};
  75. analyze_pair(_) -> invalid.
  76. analyze_triple([{_, N}, {_, N}, {_, N}]) -> {triple, card_value({any, N})};
  77. analyze_triple(_) -> invalid.
  78. analyze_four_cards(Cards) ->
  79. case group_cards(Cards) of
  80. #{4 := [Value]} -> {bomb, card_value({any, Value})};
  81. _ -> analyze_four_with_two(Cards)
  82. end.
  83. analyze_complex_pattern(Cards) ->
  84. case is_straight(Cards) of
  85. true -> {straight, highest_card_value(Cards)};
  86. false -> analyze_other_patterns(Cards)
  87. end.
  88. %% 获取游戏状态
  89. get_game_state(GameId) ->
  90. % 简单实现,实际应该从某种存储中获取
  91. {ok, #game_state{}}.
  92. %% 初始化游戏状态
  93. init_game_state() ->
  94. % 创建一个空的游戏状态
  95. #game_state{
  96. players = [],
  97. current_player = undefined,
  98. last_play = [],
  99. played_cards = [],
  100. stage = waiting,
  101. landlord_cards = []
  102. }.
  103. %% 判断出牌是否有效
  104. is_valid_play(Cards, LastPlay) ->
  105. % 如果是第一手牌,任何合法牌型都可以
  106. case LastPlay of
  107. [] -> is_valid_card_type(Cards);
  108. {_, LastCards} ->
  109. % 检查牌型是否相同且大小是否更大
  110. {ok, Type1, Value1} = get_card_type(Cards),
  111. {ok, Type2, Value2} = get_card_type(LastCards),
  112. (Type1 =:= Type2 andalso Value1 > Value2) orelse
  113. (Type1 =:= bomb andalso Type2 =/= bomb)
  114. end.
  115. %% 判断牌型是否合法
  116. is_valid_card_type(Cards) ->
  117. case get_card_type(Cards) of
  118. {ok, _, _} -> true;
  119. _ -> false
  120. end.
  121. %% 比较两手牌的大小
  122. compare_cards(Cards1, Cards2) ->
  123. {ok, Type1, Value1} = get_card_type(Cards1),
  124. {ok, Type2, Value2} = get_card_type(Cards2),
  125. if
  126. Type1 =:= bomb andalso Type2 =/= bomb -> greater;
  127. Type2 =:= bomb andalso Type1 =/= bomb -> lesser;
  128. Type1 =:= Type2 andalso Value1 > Value2 -> greater;
  129. Type1 =:= Type2 andalso Value1 < Value2 -> lesser;
  130. true -> incomparable
  131. end.
  132. %% 分配角色
  133. assign_roles(PlayerCards, LandlordIdx) ->
  134. lists:map(
  135. fun({Idx, {Pid, Cards}}) ->
  136. Role = case Idx of
  137. LandlordIdx -> landlord;
  138. _ -> farmer
  139. end,
  140. {Pid, Cards, Role}
  141. end,
  142. lists:zip(lists:seq(1, length(PlayerCards)), PlayerCards)
  143. ).
  144. %% 更新游戏状态
  145. update_game_state(GameState, PlayerPid, Cards) ->
  146. % 更新玩家手牌
  147. UpdatedPlayers = lists:map(
  148. fun({Pid, PlayerCards, Role}) when Pid =:= PlayerPid ->
  149. {Pid, PlayerCards -- Cards, Role};
  150. (Player) -> Player
  151. end,
  152. GameState#game_state.players
  153. ),
  154. % 更新出牌记录和当前玩家
  155. NextPlayer = get_next_player(GameState, PlayerPid),
  156. GameState#game_state{
  157. players = UpdatedPlayers,
  158. current_player = NextPlayer,
  159. last_play = {PlayerPid, Cards},
  160. played_cards = [{PlayerPid, Cards} | GameState#game_state.played_cards]
  161. }.
  162. %% 获取下一个玩家
  163. get_next_player(GameState, CurrentPid) ->
  164. Players = GameState#game_state.players,
  165. PlayerPids = [Pid || {Pid, _, _} <- Players],
  166. CurrentIdx = get_player_index(PlayerPids, CurrentPid),
  167. lists:nth(1 + (CurrentIdx rem length(PlayerPids)), PlayerPids).
  168. %% 获取玩家索引
  169. get_player_index(PlayerPids, TargetPid) ->
  170. {Idx, _} = lists:foldl(
  171. fun(Pid, {Idx, Found}) ->
  172. case Pid =:= TargetPid of
  173. true -> {Idx, Idx};
  174. false -> {Idx + 1, Found}
  175. end
  176. end,
  177. {1, not_found},
  178. PlayerPids
  179. ),
  180. Idx.
  181. %% 检查游戏是否结束
  182. check_game_end(GameState) ->
  183. case lists:any(
  184. fun({_, Cards, _}) -> length(Cards) =:= 0 end,
  185. GameState#game_state.players
  186. ) of
  187. true ->
  188. Winner = lists:keyfind(0, 2, GameState#game_state.players),
  189. {game_over, Winner, GameState#game_state{stage = finished}};
  190. false ->
  191. {continue, GameState}
  192. end.
  193. %% 获取玩家手牌
  194. get_player_cards(GameState, PlayerPid) ->
  195. case lists:keyfind(PlayerPid, 1, GameState#game_state.players) of
  196. {_, Cards, _} -> {ok, Cards};
  197. false -> {error, player_not_found}
  198. end.
  199. %% 检查玩家是否有这些牌
  200. has_cards(PlayerCards, CardsToPlay) ->
  201. lists:all(
  202. fun(Card) ->
  203. Count = count_card(PlayerCards, Card),
  204. CountToPlay = count_card(CardsToPlay, Card),
  205. Count >= CountToPlay
  206. end,
  207. lists:usort(CardsToPlay)
  208. ).
  209. %% 计算特定牌的数量
  210. count_card(Cards, TargetCard) ->
  211. length([Card || Card <- Cards, Card =:= TargetCard]).
  212. %% 分析四带二
  213. analyze_four_with_two(Cards) ->
  214. Grouped = group_cards(Cards),
  215. case maps:get(4, Grouped, []) of
  216. [Value] ->
  217. case length(Cards) of
  218. 6 -> {four_with_two, card_value({any, Value})};
  219. _ -> invalid
  220. end;
  221. _ -> invalid
  222. end.
  223. %% 分组牌
  224. group_cards(Cards) ->
  225. lists:foldl(
  226. fun({_, Number}, Acc) ->
  227. Count = maps:get(Number, Acc, 0) + 1,
  228. Acc#{Number => Count}
  229. end,
  230. #{},
  231. Cards
  232. ).
  233. %% 获取最高牌值
  234. highest_card_value(Cards) ->
  235. lists:max([card_value(Card) || Card <- Cards]).
  236. %% 牌值转换
  237. card_value({_, "3"}) -> 3;
  238. card_value({_, "4"}) -> 4;
  239. card_value({_, "5"}) -> 5;
  240. card_value({_, "6"}) -> 6;
  241. card_value({_, "7"}) -> 7;
  242. card_value({_, "8"}) -> 8;
  243. card_value({_, "9"}) -> 9;
  244. card_value({_, "10"}) -> 10;
  245. card_value({_, "J"}) -> 11;
  246. card_value({_, "Q"}) -> 12;
  247. card_value({_, "K"}) -> 13;
  248. card_value({_, "A"}) -> 14;
  249. card_value({_, "2"}) -> 15;
  250. card_value({_, "小王"}) -> 16;
  251. card_value({_, "大王"}) -> 17;
  252. card_value({any, Value}) -> card_value({"", Value}).
  253. %% 检查是否为顺子
  254. is_straight(Cards) ->
  255. Values = [card_value(Card) || Card <- Cards],
  256. SortedValues = lists:sort(Values),
  257. length(SortedValues) >= 5 andalso
  258. lists:max(SortedValues) =< 14 andalso % A以下的牌才能组成顺子
  259. SortedValues =:= lists:seq(hd(SortedValues), lists:last(SortedValues)).
  260. %% 分析其他牌型
  261. analyze_other_patterns(Cards) ->
  262. % 实现其他复杂牌型的分析,如飞机、连对等
  263. % 简单实现,实际应该有更复杂的逻辑
  264. invalid.
  265. %% 排序牌
  266. sort_cards(Cards) ->
  267. lists:sort(fun(A, B) -> card_value(A) =< card_value(B) end, Cards).