/// <summary> /// 打牌 /// </summary> /// <param name="player">玩家</param> /// <param name="card">要打的牌</param> /// <returns>成功状态</returns> public bool PlayCard(int player, MahjongCard card) { // 只有当前的玩家可以打牌 if (player == Playing) { // 获得当前玩家手牌 List <MahjongCard> player_onhand = GetPlayerCardOnHand(player); // 只能从手牌中打出牌来 IEnumerable <MahjongCard> card_to_play = from c in player_onhand where c == card select c; if (card_to_play.Count() == 0) { return(false); } // 玩家打出牌 GetPlayerInfo(player).PlayCard(card_to_play.First()); // 向游戏线程发送完成请求 gameStateMachine.SetState(GameStateMachine.State.SendPlayerAction); gameStateMachine.ReleaseSemaphore(); return(true); } return(false); }
/// <summary> /// 能够荣和计算(根据当前所有玩家的听牌) /// </summary> private List <RonAble> isCanRon() { // 获得刚刚打牌的玩家的牌河和打出的牌 List <MahjongCard> played_cards = GetPlayerCardPlayed(Playing); MahjongCard last_played = played_cards.Last(); // 最后一张 // 比对其他三家的手牌 List <RonAble> ronAbles = new List <RonAble>(); // 遍历所有其它玩家 for (int player = 0; player < 4; player++) { if (player == Playing) { continue; // 跳过自己 } // 获得玩家信息 PlayerInfo info = player_info[player]; // 先查询振听状态,如果振听则不能荣和 if (info.waiting_tsumo == WaitingTsumo.None) { // 查询是否是被听的牌 IEnumerable <MahjongCard> huCard = from card in info.waiting where card == last_played select card; if (huCard.Count() > 0) { ronAbles.Add(new RonAble(player, last_played)); } } } return(ronAbles); }
/// <summary> /// 向牌河中打一张牌 /// </summary> /// <param name="card">要打的牌</param> public void PlayCard(MahjongCard card) { // 将牌从手牌打到牌河 card_played.Add(card); // 从手牌中删除这张牌 card_onhand.Remove(card); }
public static void InitializeMahjongClass() { Assembly assembly = Assembly.GetExecutingAssembly(); string Path = (from path in assembly.GetManifestResourceNames() where path.Contains(ResoursePath) select path).First(); Stream xStream = assembly.GetManifestResourceStream(Path); XElement xElement = XElement.Load(xStream); IEnumerable <XElement> cards = xElement.Elements("cards").Elements("card"); CardInfo.Clear(); foreach (XElement card in cards) { MahjongCard mahjongCard = new MahjongCard { name = (MahjongCardName)Enum.Parse(typeof(MahjongCardName), card.Element("name").Value), type = (MahjongCardType)Enum.Parse(typeof(MahjongCardType), card.Element("type").Value), c_name = card.Element("c_name").Value, yao9 = card.Element("yao9").Value.Equals("true") ? true : false, group = (MahjongCardGroupType)Enum.Parse(typeof(MahjongCardGroupType), card.Element("group").Value), }; if (mahjongCard.yao9) { mahjongCard.treasure = 0; } else { mahjongCard.treasure = card.Element("treasure").Value.Equals("true") ? 1 : 0; } if (mahjongCard.type == MahjongCardType.Char) { mahjongCard.squad = (MahjongCardSquadType)Enum.Parse(typeof(MahjongCardSquadType), card.Element("squad").Value); mahjongCard.grade = (MahjongCardGradeType)Enum.Parse(typeof(MahjongCardGradeType), $"G{card.Element("grade").Value}"); } CardInfo.Add(mahjongCard); } IEnumerable <XElement> yakus = xElement.Elements("yakus").Elements("yaku"); YakuInfo.Clear(); foreach (XElement yaku in yakus) { MahjongYaku mahjongYaku = new MahjongYaku() { type = (MahjongYakuType)Enum.Parse(typeof(MahjongYakuType), yaku.Element("name").Value), c_name = yaku.Element("c_name").Value, level = Convert.ToInt32(yaku.Element("level").Value), no_furu = yaku.Element("nofuru").Value.Equals("true") ? true : false, furu_n = yaku.Element("furun").Value.Equals("true") ? true : false, }; YakuInfo.Add(mahjongYaku); } }
/// <summary> /// 根据指示牌添加宝牌 /// </summary> /// <param name="indicator">指示牌</param> private void _add_treasure_card(MahjongCard indicator) { int t = 0; // 将指示牌添加到指示牌列表 card_indicator.Add(indicator); // 获得指示牌的下一张牌 if (indicator.type == MahjongCardType.Char) { // 如果是角色牌,则使用官方排序 // 每种角色9张 t = (int)indicator.name + 1; if ((t == 0x0A) || (t == 0x14) || (t == 0x1E)) { t -= 9; } } else { // 如果是应援角色牌,团体牌,则使用自定义排序 t = (int)indicator.name + 1; if (t == 0x22) { t -= 3; } if (t == 0x25) { t -= 2; } if (t == 0x31) { t -= 5; } } // 从所有的牌中找到宝牌,将其的宝牌等级+1 // 先从牌山找 for (int i = 0; i < card_stacks.Count(); i++) { // 确认宝牌 if (t == (int)card_stacks[i].name) { card_stacks[i].AddTreasure(); } } // 从手牌和副露里找 for (int i = 0; i < 4; i++) { player_info[i].SetTreasureCard((MahjongCardName)t); } }
/// <summary> /// 摸牌(从牌山取得一张牌) /// </summary> private MahjongCard TouchCard() { // 获得牌山顶的牌 MahjongCard card = card_stacks.First(); // 从牌山中删除这张牌 card_stacks.RemoveAt(0); // 只要摸牌就加巡数 round++; // 返回所需的牌 return(card); }
/// <summary> /// 创建一个可荣牌组 /// </summary> public RonAble(int playerId, MahjongCard RonCard) { this.playerId = playerId; this.RonCard = RonCard; }
/// <summary> /// 向手牌中加入一张牌 /// </summary> /// <param name="card">要加入的牌</param> public void AddHandCard(MahjongCard card) => card_onhand.Add(card);
private void GamingThread() { // 多线程状态机 for (; ;) { // 等待信号量 gameStateMachine.WaitSemaphore(); switch (gameStateMachine.status) { case GameStateMachine.State.Idle: break; case GameStateMachine.State.WaitPlayerAction: break; case GameStateMachine.State.SendPlayerAction: // 创建响应 List <PlayerAction> playerActions = new List <PlayerAction>(); // 判定荣和 List <RonAble> RonAbles = isCanRon(); if (RonAbles.Count() > 0) { // 发出可以荣和的消息到玩家 // 先根据玩家编号分类 IEnumerable <RonAble> player_ronable = from ronable in RonAbles group ronable by ronable.playerId into g select g.First(); foreach (RonAble ronable in player_ronable) { playerActions.Add(new PlayerAction(ronable)); } } // 判定鸣牌的可能性 List <FuruAble> Furuables = isCanFuru(); if (Furuables.Count > 0) { // 发出可以鸣牌的消息到玩家 // 先根据玩家编号分类 IEnumerable <FuruAble> player_furuable = from furuable in Furuables group furuable by furuable.playerId into g select g.First(); foreach (FuruAble furuable in player_furuable) { foreach (MahjongCardFuru furu in furuable.FuruableList) { playerActions.Add(new PlayerAction(furu, furuable.playerId)); } } } // 清空动作预备列表 PrepareActionsList.Clear(); // 打开玩家动作通道 gameStateMachine.OpenPlayerActionChannel(); if (playerActions.Count > 0) { // 保存可能的玩家操作列表 PlayerActionsList = (from act in playerActions group act by act.playerId into g select g).ToList(); // 获得用户响应,将向其他用户广播其响应 // 使用响应回调函数 PlayerActionResponseCallback(playerActions); // 进入接受用户响应的状态 gameStateMachine.SetState(GameStateMachine.State.AcceptingPlayerAction); } else { // 进入用户响应执行状态(跳过) AcceptedPlayerActions.Clear(); gameStateMachine.SetState(GameStateMachine.State.ExecutePlayerAction); gameStateMachine.ReleaseSemaphore(); } break; case GameStateMachine.State.AcceptingPlayerAction: // 取出队列 PlayerAction action = gameStateMachine.GetPlayerAction(); // 加入动作列表 PrepareActionsList.Add(action); // 从可用列表中删除所有该玩家其他操作 PlayerActionsList.RemoveAll((match) => match.Key == action.playerId); // 遍历其他所有动作进行优先级比对 bool isHighestPriority = true; foreach (IGrouping <int, PlayerAction> player in PlayerActionsList) { foreach (PlayerAction player_act in player) { if (player_act.Priority < action.Priority) { isHighestPriority = false; break; } } if (isHighestPriority) { break; } } if (isHighestPriority || (PlayerActionsList.Count == 0)) { // 最高优先级 // 关闭动作通道 gameStateMachine.ClosePlayerActionChannel(); // 处理动作列表 int highestPriority = PrepareActionsList.Min((selector) => (selector.Priority)); IEnumerable <PlayerAction> accept_list = PrepareActionsList.FindAll((match) => (match.Priority == highestPriority) && (match.actionType != PlayerActionType.Cancel)); IEnumerable <PlayerAction> refuse_list = PrepareActionsList.Except(accept_list); AcceptedPlayerActions = accept_list.ToList(); foreach (PlayerAction act in accept_list) { // 接受这些请求 PlayerActionAcceptedCallback(act.playerId, true); } foreach (PlayerAction act in refuse_list) { // 拒绝/取消这些请求 PlayerActionAcceptedCallback(act.playerId, false); } // 将没有反应(被忽略)的玩家拒绝 for (int i = 0; i < 4; i++) { if (i == Playing) { continue; } if (PrepareActionsList.FindIndex((match) => match.playerId == i) < 0) { PlayerActionAcceptedCallback(i, false); } } // 进入执行玩家操作状态 gameStateMachine.SetState(GameStateMachine.State.ExecutePlayerAction); gameStateMachine.ReleaseSemaphore(); } break; case GameStateMachine.State.ExecutePlayerAction: if (AcceptedPlayerActions.Count == 0) { // 没有接受的操作,直接跳转到下家 _NextPlayer(); gameStateMachine.SetState(GameStateMachine.State.Idle); } else { foreach (PlayerAction act in AcceptedPlayerActions) { switch (act.actionType) { case PlayerActionType.Ron: case PlayerActionType.Tsumo: // 和牌 break; case PlayerActionType.ChiGrade: case PlayerActionType.ChiSquad: case PlayerActionType.Pong: case PlayerActionType.Kong: case PlayerActionType.Kong_Self: case PlayerActionType.Kong_Add: // 副露 // 获得刚刚打牌的玩家的牌河和打出的牌 List <MahjongCard> played_cards = GetPlayerCardPlayed(Playing); MahjongCard last_played = played_cards.Last(); // 最后一张 // 副露对象和副露者 int target = Playing; int furu_player = act.playerId; // 新的副露 MahjongCardFuru furu = new MahjongCardFuru() { target = target, type = ActionTypeToFuruType(act.actionType), cards = new List <MahjongCard>(), }; // 组成副露牌组 foreach (MahjongCard card in act.effectCards) { furu.cards.Add(card); // 从手牌中删除 player_info[furu_player].card_onhand.Remove(card); } furu.cards.Add(last_played); // 添加到玩家副露 player_info[furu_player].card_furu.Add(furu); // 从目标玩家牌河删除 player_info[target].card_played.RemoveAt(player_info[target].card_played.Count - 1); // 直接跳转到副露家 _ToPlayer(furu_player); gameStateMachine.SetState(GameStateMachine.State.Idle); break; } } } break; case GameStateMachine.State.Exit: return; } } }
/// <summary> /// 能够鸣牌计算 /// </summary> private List <FuruAble> isCanFuru() { // 获得刚刚打牌的玩家的牌河和打出的牌 List <MahjongCard> played_cards = GetPlayerCardPlayed(Playing); MahjongCard last_played = played_cards.Last(); // 最后一张 // 比对其他三家的手牌 List <FuruAble> furuAbles = new List <FuruAble>(); // 记录是否有玩家可以碰或者杠以减少不必要的计算 bool hasPongKong = false; // 遍历所有其它玩家 for (int player = 0; player < 4; player++) { if (player == Playing) { continue; // 跳过自己 } // 获得手牌 List <MahjongCard> player_hand = GetPlayerCardOnHand(player); // 记录可副露牌组 FuruAble furuAble = new FuruAble(player); // 如果已经有别的玩家可以碰杠,则不可能再有玩家可以碰杠 if (hasPongKong == false) { // 优先找出杠子和刻子 IEnumerable <MahjongCard> PongKong = from card in player_hand where card == last_played select card; if (PongKong.Count() >= 2) { hasPongKong = true; // 可以碰 furuAble.FuruableList.Add(new MahjongCardFuru() { cards = Enumerable.Repeat(last_played, 2).ToList(), target = Playing, type = FuruType.Pong, }); if (PongKong.Count() == 3) { // 可以杠 furuAble.FuruableList.Add(new MahjongCardFuru() { cards = Enumerable.Repeat(last_played, 3).ToList(), target = Playing, type = FuruType.Kong, }); hasPongKong = true; } } } if (last_played.type == MahjongCardType.Char) { // 年级和小组顺子(吃) // 年级顺子 MahjongCardName name = last_played.name; MahjongCardGradeType grade = last_played.grade; MahjongCardGroupType group = last_played.group; IEnumerable <IGrouping <MahjongCardName, MahjongCard> > grade_chi = from card in player_hand where (card.grade == grade) && (card.name != name) && (card.type == MahjongCardType.Char) && (card.@group == @group) group card by card.name into g select g; if (grade_chi.Count() == 2) { // 至少要有两种同年级的牌才可以吃 // 获得要吃的牌 List <MahjongCard> chi = new List <MahjongCard>(); foreach (IGrouping <MahjongCardName, MahjongCard> cards in grade_chi) { chi.Add(cards.First()); } // 加入可副露列表 furuAble.FuruableList.Add(new MahjongCardFuru() { cards = chi, target = Playing, type = FuruType.ChiGrade, }); } // 小组顺子 MahjongCardSquadType squad = last_played.squad; IEnumerable <IGrouping <MahjongCardName, MahjongCard> > squad_chi = from card in player_hand where (card.squad == squad) && (card.name != name) && (card.type == MahjongCardType.Char) group card by card.name into g select g; if (squad_chi.Count() == 2) { // 至少要有两种同小组的牌才可以吃 // 获得要吃的牌 List <MahjongCard> chi = new List <MahjongCard>(); foreach (IGrouping <MahjongCardName, MahjongCard> cards in squad_chi) { chi.Add(cards.First()); } // 加入可副露列表 furuAble.FuruableList.Add(new MahjongCardFuru() { cards = chi, target = Playing, type = FuruType.ChiSquad, }); } } furuAbles.Add(furuAble); } return(furuAbles); }
/// <summary> /// 和牌判定(除去雀头和副露) /// </summary> /// <param name="cards">手牌</param> /// <param name="start">从第几张手牌开始找刻子</param> /// <returns>是否和牌</returns> private bool isHu(List <MahjongCard> cards, int start, ref List <HuCard> huCard) { if (cards.Count == 0) { return(true); // 空牌和 } if (cards.Count < 3) { return(false); // 剩牌不和 } // 针对第一张牌开始寻找刻子和顺子 IEnumerable <MahjongCard> u = from card in cards where card.name == cards[start].name select card; int count = u.Count(); if (count >= 3) { // 将刻子加入牌组 HuCard hucard = new HuCard() { type = HuCardType.PongKong, furu = false, cards = new List <MahjongCard>() { cards[start], cards[start + 1], cards[start + 2] }, }; huCard.Add(hucard); // 删除这个刻子 for (int m = 0; m < 3; m++) { cards.RemoveAt(start); } // 递归判和 return(isHu(cards, ref huCard)); } else { if (start + 1 < cards.Count) { return(isHu(cards, start + 1, ref huCard)); } else { // 寻找顺子(年级和小组顺子) MahjongCard cur_card = u.First(); // 获得牌种 MahjongCardType type = cur_card.type; // 只有角色牌才可以凑成年级或小组顺子 if (type == MahjongCardType.Char) { // 获得团体、年级和小组信息 MahjongCardGroupType group = cur_card.group; MahjongCardGradeType grade = cur_card.grade; MahjongCardSquadType squad = cur_card.squad; // 确认同团体的牌有至少三张 IEnumerable <MahjongCard> same_group = from card in cards where (card.name != cur_card.name) && (card.@group == @group) && (card.type == MahjongCardType.Char) select card; if (same_group.Count() >= 2) { // 判断同年级/同小组 IEnumerable <MahjongCard> same_grade = from card in same_group where card.grade == grade select card; IEnumerable <MahjongCard> same_squad = from card in same_group where card.squad == squad select card; IEnumerable <MahjongCard> determine; bool GradeOrSquad = false; if (same_grade.Count() >= 2) { GradeOrSquad = false; determine = same_grade; // 判断同年级 } else if (same_squad.Count() >= 2) { GradeOrSquad = true; determine = same_squad; // 判断同小队 } else { return(false); } // 每种牌只找出一张 IEnumerable <MahjongCard> not_same = from card in determine group card by card.name into g select g.First(); if (not_same.Count() >= 2) { MahjongCard[] not_same_cards = not_same.ToArray(); // 将顺子加入牌组 HuCard hucard = new HuCard() { type = (GradeOrSquad) ? HuCardType.SquadChi : HuCardType.GradeChi, furu = false, cards = new List <MahjongCard>() { cur_card, not_same_cards[0], not_same_cards[1] }, }; huCard.Add(hucard); // 从手牌中删除 bool p = true, q = true, r = true; // 防止重复删除 for (int i = 0; i < cards.Count; i++) { if ((cards[i].name == cur_card.name) && p) { p = false; cards.RemoveAt(i); i--; continue; } if ((cards[i].name == not_same_cards[0].name) && q) { q = false; cards.RemoveAt(i); i--; continue; } if ((cards[i].name == not_same_cards[1].name) && r) { r = false; cards.RemoveAt(i); i--; continue; } if (!(p || q || r)) { break; } } // 递归判和 return(isHu(cards, ref huCard)); } } } } return(false); } }