feat: initial commit - HJHA game server full source

6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) +
game logic (GameFix/GameDAL/ServerData) +
network layer (NetWorkMessage) +
data layer (ObjectModel) +
utilities (MrWu/Core/Config/CloudAPI/dll) +
adapters (zyxAdapter/base)

.NET 8.0 C# solution, 16 projects, 958 source files
This commit is contained in:
2026-07-07 12:02:15 +08:00
commit e9616125ce
958 changed files with 158203 additions and 0 deletions

218
GameFix/Common/Card/Card.cs Normal file
View File

@ -0,0 +1,218 @@
using System;
using System.Linq;
namespace GameFix.Poker
{
/// <summary>
/// 一张牌
/// </summary>
public class Card
{
public int Id { get; }
public byte CardType { get; set; }
/// <summary>
/// 逻辑值
/// </summary>
public byte Value { get; private set; }
/// <summary>
/// 花色
/// </summary>
public byte Suit { get; private set; }
/// <summary>
/// 排序数
/// </summary>
public int SortNum { get;private set; }
/// <summary>
/// 原始逻辑值
/// </summary>
public byte OriginValue { get; private set; }
/// <summary>
/// 用户数据
/// </summary>
public object UserData { get; set; }
public override string ToString()
{
var suitStr = string.Empty;
switch (Suit)
{
case DataCardConst.SuitSpade:
suitStr = "黑";
break;
case DataCardConst.SuitHeart:
suitStr = "红";
break;
case DataCardConst.SuitClub:
suitStr = "梅";
break;
case DataCardConst.SuitDiamond:
suitStr = "方";
break;
case DataCardConst.SuitJoker:
return (Value == DataCardConst.JokerValues[0] ? "小王" : "大王");
default:
return $"CardErr[{Suit}_{Value}]";
}
var vIndex = Array.IndexOf(DataCardConst.CardValues, Value);
if (vIndex<0 || vIndex >= DataCardConst.CardValueText.Length)
return $"CardErr[{Suit}_{Value}]";
var valueStr = DataCardConst.CardValueText[vIndex];
return suitStr + valueStr;
}
int? _hashCode = null;
public override int GetHashCode() =>
_hashCode.HasValue ? _hashCode.Value : (_hashCode = Id << 16 | (byte)Suit << 8 | Value).Value;
public Card()
{
}
public Card(int id, byte suit, byte value)
{
this.Id = id;
this.Suit = suit;
this.Value = value;
this.SortNum = Value * 10 + suit;
}
public void SetSortNum(int num)
{
this.SortNum = num;
}
public bool IsLz => CardType == DataCardConst.CardTypeLZ;
public bool IsReplace => OriginValue != 0;
/// <summary>
/// 是否包含某个类型
/// </summary>
public bool IsCardType(byte cardType)
{
return (CardType & cardType) == cardType;
}
/// <summary>
/// 附加一个牌值类型
/// </summary>
public void AddOrRmCardType(byte cardType, bool isAdd = true)
{
if (isAdd)
{
CardType |= cardType;
}
else
{
CardType &= (byte)~cardType;
}
}
/// <summary>
/// 癞子可以替换任何牌
/// </summary>
public Card Replace(byte value)
{
if (CardType == DataCardConst.CardTypeLZ)
{
var newCard = this.Clone();
newCard.OriginValue = Value;
newCard.Value = value;
newCard.SortNum = Value * 10 + Suit;
return newCard;
}
return this;
}
/// <summary>
/// 浅拷贝
/// </summary>
public Card Clone() => (Card)MemberwiseClone();
}
public static class CardManager
{
/// <summary>
/// 逻辑数 转 显示数
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static byte LogicValue2UINum(byte value)
{
switch (value)
{
case 14:
return 1;
case 16:
return 2;
case 18:
return 14;
case 19:
return 15;
default:
return value;
}
}
public static byte LogicValue2SoundNum(byte value)
{
switch (value)
{
case 14:
return 14;
case 16:
return 15;
case 18:
return 16;
case 19:
return 17;
default:
return value;
}
}
public static (byte, byte) GetSuitValueFromStr(string cardString)
{
var chars = cardString.Trim().ToCharArray();
var pos = 0;
if (char.IsNumber(chars[0])) {
pos++;
}
byte suit = DataCardConst.SuitJoker;
switch (chars[pos]) {
case '小': return (DataCardConst.SuitJoker, 18);
case '大': return (DataCardConst.SuitJoker, 19);
case '黑': suit = DataCardConst.SuitSpade; break;
case '梅': suit = DataCardConst.SuitClub; break;
case '红': suit = DataCardConst.SuitHeart; break;
case '方': suit = DataCardConst.SuitDiamond; break;
default: throw new NotSupportedException($"不支持的花色:{chars[0]}");
}
pos++;
var value = byte.MinValue;
var str = new string(chars.Skip(pos).ToArray()).TrimStart('0').TrimEnd(' ');
var index = Array.IndexOf(DataCardConst.CardValueText, str);
if (index >= 0)
{
value = DataCardConst.CardValues[index];
}
else
{
value = byte.Parse(str);
}
return (suit, value);
}
}
}

View File

@ -0,0 +1,219 @@
using System.Collections.Generic;
using System.Linq;
namespace GameFix.Poker
{
public abstract class CardBaseGroup
{
// key-牌值 value-牌组信息 (不含癞子)
protected List<CardGroupList> mCardGroupList = new List<CardGroupList>(20); // 大王19 (按照牌值合并的牌组)
// key-牌数量 value-这个数量对应的所有牌组 (不含癞子)
protected Dictionary<int, List<CardGroupList>> mCardGroupDic;
protected List<Card> mCards; // 原始的牌数据
protected List<Card> mLZCards; // 癞子牌
protected List<Card> mNoLZCards;// 除去癞子的牌列表
internal IStyleComparer mStyleComparer;
internal Deck mDeck;
protected List<CardStyleBaseHelper> mCardStyleBaseHelpers = new List<CardStyleBaseHelper>();
public List<Card> SelfCards => mCards;
public List<CardGroupList> SelfCardGroupList => mCardGroupList;
public Dictionary<int, List<CardGroupList>> SelfCardGroupDic => mCardGroupDic;
public List<Card> Cards
{
get
{
// 如果是手牌对象
if (this is CardHandGroup cardHandGroup)
{
// 并且有需要检测得牌就取CheckCardGroup得牌
if (cardHandGroup.CheckCardGroup?.mCards.Count > 0)
return cardHandGroup.CheckCardGroup.mCards;
}
return mCards;
}
}
public List<Card> LZCards
{
get
{
// 如果是手牌对象
if (this is CardHandGroup cardHandGroup)
{
// 并且有需要检测得牌就取CheckCardGroup得牌
if (cardHandGroup.CheckCardGroup?.mLZCards.Count > 0)
return cardHandGroup.CheckCardGroup.mLZCards;
}
return mLZCards;
}
}
public List<Card> NoLZCards
{
get
{
// 如果是手牌对象
if (this is CardHandGroup cardHandGroup)
{
// 并且有需要检测得牌就取CheckCardGroup得牌
if (cardHandGroup.CheckCardGroup?.mNoLZCards.Count > 0)
return cardHandGroup.CheckCardGroup.mNoLZCards;
}
return mNoLZCards;
}
}
public List<CardGroupList> CardGroupList
{
get
{
// 如果是手牌对象
if (this is CardHandGroup cardHandGroup)
{
// 并且有需要检测得牌就取CheckCardGroup得牌
if (cardHandGroup.CheckCardGroup?.mCardGroupList.Count > 0)
return cardHandGroup.CheckCardGroup.mCardGroupList;
}
return mCardGroupList;
}
}
public Dictionary<int, List<CardGroupList>> CardGroupDic
{
get
{
// 如果是手牌对象
if (this is CardHandGroup cardHandGroup)
{
// 并且有需要检测得牌就取CheckCardGroup得牌
if (cardHandGroup.CheckCardGroup?.mCardGroupDic.Count > 0)
return cardHandGroup.CheckCardGroup.mCardGroupDic;
}
return mCardGroupDic;
}
}
protected CardBaseGroup(IEnumerable<Card> cards, Deck deck)
{
mDeck = deck;
mCards = new List<Card>(cards);
mCards.Sort(new CardDefaultComparer(true));
mLZCards = new List<Card>(mCards.Count);
mNoLZCards = new List<Card>(mCards.Count);
InitPreData();
}
void InitPreData()
{
mCardGroupList.Clear();
mLZCards.Clear();
mNoLZCards.Clear();
for (int i = 0; i < mCardGroupList.Capacity; i++)
{
mCardGroupList.Add(new CardGroupList());
}
for (int i = 0; i < mCards.Count; i++)
{
if (mCards[i].CardType == DataCardConst.CardTypeLZ)
{
mLZCards.Add(mCards[i]);
continue;
}
mCardGroupList[mCards[i].Value].Add(mCards[i]);
mNoLZCards.Add(mCards[i]);
}
mCardGroupDic = mCardGroupList
.GroupBy(g=>g.Count)
.Where(g=>g.Key != 0)
.ToDictionary(g=>g.Key, g=>g.ToList());
}
internal CardBaseGroup RegisterHelper(List<CardStyleBaseHelper> helpers)
{
mCardStyleBaseHelpers.AddRange(helpers);
return this;
}
internal CardBaseGroup RegisterStyleComparer(IStyleComparer comparer)
{
mStyleComparer = comparer;
return this;
}
public CardBaseGroup RemoveCards(IEnumerable<Card> cards)
{
mCards.RemoveAll(cards.Contains);
InitPreData();
return this;
}
public CardBaseGroup RemoveCards(IEnumerable<CardGroupList> cardGroups)
{
var cards = cardGroups.SelectMany(x => x).ToList();
RemoveCards(cards);
return this;
}
public CardBaseGroup AddHelper(CardStyleBaseHelper helper)
{
mCardStyleBaseHelpers.Add(helper);
return this;
}
/// <summary>
/// 获取一种牌型的数量
/// </summary>
/// <returns>数量</returns>
public int GetSingleCardStyleCount(int cardValue)
{
if (cardValue >= mCardGroupList.Capacity)
return 0;
return mCardGroupList[cardValue].Count;
}
public CardGroupList GetAllJoker()
{
CardGroupList list = new CardGroupList();
list.AddRange(mCardGroupList[DataCardConst.CardValueBigJoker]);
list.AddRange(mCardGroupList[DataCardConst.CardValueSmallJoker]);
return list;
}
public bool HaveJoker()
{
return mCards.Any(x => DataCardConst.JokerValues.Contains(x.Value));
}
public int GetJokerCount()
{
return mCards.Count(x => DataCardConst.JokerValues.Contains(x.Value));
}
public bool IsAllJoker()
{
return CardFunc.IsAllJoker(mCards);
}
public bool IsAllSuitSame()
{
return CardFunc.IsAllSuitSame(mCards);
}
public bool IsAllValueSame()
{
return CardFunc.IsAllValueSame(mCards);
}
protected CardStyleBaseHelper GetHelper(int cardStyle)
{
return mCardStyleBaseHelpers.Find(x => x.CardStyle == cardStyle);
}
}
}

View File

@ -0,0 +1,111 @@
using System.Collections.Generic;
using GameMessage;
namespace GameFix.Poker
{
// 默认降序比较器
public class CardDefaultComparer :IComparer<Card>
{
/// <summary>
/// 是否升序
/// </summary>
bool mIsAscending;
public CardDefaultComparer(bool isAscending = false)
{
mIsAscending = isAscending;
}
public int Compare(Card x, Card y)
{
if (x == null && y == null) return 0;
if (x == null) return 1; // 空值放后面
if (y == null) return -1;
if (x.CardType == DataCardConst.CardTypeLZ && y.CardType != DataCardConst.CardTypeLZ) return -1;
if (y.CardType == DataCardConst.CardTypeLZ && x.CardType != DataCardConst.CardTypeLZ) return 1;
return mIsAscending ? -y.SortNum.CompareTo(x.SortNum) : y.SortNum.CompareTo(x.SortNum);
}
}
public class CardStyleComparer : IComparer<Card>
{
private CardStyleInfo mCardStyleInfo;
public CardStyleComparer(CardStyleInfo cardStyleInfo)
{
mCardStyleInfo = cardStyleInfo;
}
public int Compare(Card x, Card y)
{
if (x == null && y == null) return 0;
if (x == null) return 1; // 空值放后面
if (y == null) return -1;
if (x.Value < mCardStyleInfo.CardValue && y.Value >= mCardStyleInfo.CardValue) return 1;
if (x.Value >= mCardStyleInfo.CardValue && y.Value < mCardStyleInfo.CardValue) return 1;
return x.SortNum.CompareTo(y.SortNum);
}
}
public class CardIdComparer : IComparer<Card>
{
public int Compare(Card x, Card y)
{
if (ReferenceEquals(x, y)) return 0;
if (y is null) return 1;
if (x is null) return -1;
return x.Id.CompareTo(y.Id);
}
}
/// <summary>
/// 相同牌值放一起
/// </summary>
public class CardColorSuitComparer : IComparer<Card>
{
private List<int> mColorSuitList = new List<int>()
{ DataCardConst.SuitSpade, DataCardConst.SuitClub, DataCardConst.SuitHeart, DataCardConst.SuitDiamond };
public int Compare(Card x, Card y)
{
if (ReferenceEquals(x, y)) return 0;
if (y is null) return 1;
if (x is null) return -1;
// 先按牌值排序(降序)
int result = y.Value.CompareTo(x.Value);
if (result != 0) return result;
// 相同牌值按花色排序
int xIndex = mColorSuitList.IndexOf(x.Suit);
int yIndex = mColorSuitList.IndexOf(y.Suit);
if (xIndex == -1) xIndex = int.MaxValue;
if (yIndex == -1) yIndex = int.MaxValue;
result = xIndex.CompareTo(yIndex);
return result != 0 ? result : y.SortNum.CompareTo(x.SortNum);
}
}
public class CardValueEqualityComparer : IEqualityComparer<Card>
{
public bool Equals(Card x, Card y)
{
if (ReferenceEquals(x, y)) return true;
if (x is null) return false;
if (y is null) return false;
if (x.GetType() != y.GetType()) return false;
return x.Value == y.Value && x.Suit == y.Suit;
}
public int GetHashCode(Card obj)
{
unchecked
{
var hashCode = obj.Value.GetHashCode();
hashCode = (hashCode * 397) ^ obj.Suit.GetHashCode();
return hashCode;
}
}
}
}

View File

@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace GameFix.Poker
{
public static class CardFunc
{
/// <summary>
/// 获取牌值分数 (5 10 K)
/// </summary>
public static int GetCard510KScore(List<Card> cards)
{
int score = 0;
for (int i = 0; i < cards.Count; i++)
{
if (cards[i].Value == DataCardConst.CardValue5)
score += 5;
if (cards[i].Value == DataCardConst.CardValue10)
score += 10;
if (cards[i].Value == DataCardConst.CardValueK)
score += 10;
}
return score;
}
/// <summary>
/// 获取cards 中所有510k的集合
/// </summary>
public static List<List<Card>> Get510KList(List<Card> cards, int lessNumber)
{
var result = new List<List<Card>>();
var valueCardDict = cards.GroupBy(x => x.Value)
.ToDictionary(g => g.Key, g => g.ToList());
valueCardDict.TryGetValue(DataCardConst.CardValue5, out var cards5);
valueCardDict.TryGetValue(DataCardConst.CardValue10, out var cards10);
valueCardDict.TryGetValue(DataCardConst.CardValueK, out var cardsK);
if (cards5?.Count >= lessNumber && cards10?.Count >= lessNumber && cardsK?.Count >= lessNumber)
{
var min = Math.Min(Math.Min(cards5.Count, cards10.Count), cardsK.Count);
for (int i = 0; i < min; i++)
{
var list = new List<Card>
{
cards5[i],
cards10[i],
cardsK[i]
};
result.Add(list);
}
}
return result;
}
public static int GetJokerCount(List<Card> cards)
{
return cards.Count(DataCardConst.IsJoker);
}
public static bool IsAllJoker(List<Card> cards)
{
if (cards.Count == 0) return false;
if (cards.Count == 1) return true;
for (int i = 1; i < cards.Count; i++)
{
if (!DataCardConst.IsJoker(cards[i]))
{
return false;
}
}
return true;
}
/// <summary>
/// 获取最大得同色数量
/// </summary>
/// <param name="cards"></param>
/// <returns></returns>
public static int GetMaxSameColorCount(List<Card> cards)
{
return cards.Select(x => GetSuitColorType(x.Suit))
.GroupBy(x => x).Select(g => g.Count()).DefaultIfEmpty(0).Max();
}
/// <summary>
/// 获取最大同牌值数量
/// </summary>
public static int GetMaxSameValueCount(List<Card> cards)
{
return cards.Select(x => x.Value)
.GroupBy(x => x).Select(g => g.Count()).DefaultIfEmpty(0).Max();
}
/// <summary>
/// 所有颜色是否相同 (红色/黑色)
/// </summary>
public static bool IsAllColorSame(List<Card> cards)
{
if (cards.Count == 0) return false;
if (cards.Count == 1) return true;
for (int i = 1; i < cards.Count; i++)
{
if (GetSuitColorType(cards[i].Suit) != GetSuitColorType(cards[i-1].Suit))
{
return false;
}
}
return true;
}
public static int GetSuitColorType(int suit)
{
if (suit == DataCardConst.SuitSpade || suit == DataCardConst.SuitClub)
return 1;
if (suit == DataCardConst.SuitHeart || suit == DataCardConst.SuitDiamond)
return 2;
return 0;
}
/// <summary>
/// 所有花色是否相同
/// </summary>
public static bool IsAllSuitSame(List<Card> cards)
{
if (cards.Count == 0) return false;
if (cards.Count == 1) return true;
for (int i = 1; i < cards.Count; i++)
{
if (cards[i].Suit != cards[i-1].Suit)
{
return false;
}
}
return true;
}
/// <summary>
/// 所有牌值是否相同
/// </summary>
public static bool IsAllValueSame(List<Card> cards)
{
if (cards.Count == 0) return false;
if (cards.Count == 1) return true;
for (int i = 1; i < cards.Count; i++)
{
if (cards[i].Value != cards[i-1].Value)
{
return false;
}
}
return true;
}
}
}

View File

@ -0,0 +1,219 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 手牌组相关逻辑
/// </summary>
public class CardGroup : CardBaseGroup
{
public CardGroup(IEnumerable<Card> cards, Deck deck) : base(cards, deck)
{
}
/// <summary>
/// 拆解牌组
/// </summary>
public List<CardGroupList> Decompose()
{
List<CardGroupList> ret = new List<CardGroupList>();
var cardGroup = mDeck.CreateCardGroup(mCards);
foreach (var helper in mCardStyleBaseHelpers)
{
var styleCards = helper.GetAllStyleCards(cardGroup, false);
ret.AddRange(styleCards);
cardGroup.RemoveCards(styleCards);
}
if (cardGroup.mCards.Count > 0)
{ // 还有未知牌型没有拆解完? (理论不存在)
ret.Add(new CardGroupList(cardGroup.mCards));
}
return ret;
}
/// <summary>
/// 获取吃牌提示
/// </summary>
public List<CardGroupList> GetEatTip(CardStyleInfo targetStyle)
{
List<CardGroupList> ret = new List<CardGroupList>();
if (targetStyle == null)
{
// 没有提供对方牌型,先判断自己手牌是否符合一种牌型可以一次性出完
var styleInfo = GetCardStyleInfo();
bool isCanOutImmediate = false;
if (styleInfo.CardStyle != DataCardConst.CardStyle0)
{
var cardGroupList = new CardGroupList(mCards);
cardGroupList.CardStyleInfo = styleInfo;
ret.Add(cardGroupList);
isCanOutImmediate = true;
}
if (!isCanOutImmediate)
{
foreach (var helper in mCardStyleBaseHelpers)
{
ret.AddRange(helper.GetAllEatTipGroup(this, null));
if (ret.Count > 0)
break;
}
}
}
else
{
foreach (var helper in mCardStyleBaseHelpers)
{
if (mStyleComparer.Greater(helper.CardStyle, targetStyle.CardStyle))
{
ret.AddRange(helper.GetAllEatTipGroup(this, targetStyle));
}
}
// 没找到再尝试有用癞子去匹配
if (ret.Count == 0 && mLZCards.Count > 0)
{
foreach (var helper in mCardStyleBaseHelpers)
{
if (mStyleComparer.Greater(helper.CardStyle, targetStyle.CardStyle))
{
ret.AddRange(helper.GetAllEatTipGroupWithLZ(this, targetStyle));
}
}
}
}
// 牌类型赋值
foreach (var cardList in ret)
{
if (cardList.CardStyleInfo != null) continue;
var group = mDeck.CreateCardGroup(cardList);
cardList.CardStyleInfo = group.GetCardStyleInfo();
}
return ret;
}
public List<CardGroupList> GetShunTip(CardStyleInfo targetStyle, byte[] tipHelpers)
{
List<CardGroupList> ret = new List<CardGroupList>();
for (int i = 0; i < tipHelpers.Length; i++)
{
var helper = GetHelper(tipHelpers[i]);
var tipCardList = helper?.GetAllEatTipGroup(this, targetStyle);
if (tipCardList != null && tipCardList.Count > 0)
{
ret.AddRange(tipCardList.OrderByDescending(x=>x.Count));
return ret;
}
}
return ret;
}
public List<CardGroupList> GetEatTip(int styleId)
{
List<CardGroupList> ret = new List<CardGroupList>();
var helper = GetHelper(styleId);
if (helper != null)
{
ret.AddRange(helper.GetAllEatTipGroup(this, null));
if (ret.Count == 0 && mLZCards.Count > 0)
{
ret.AddRange(helper.GetAllEatTipGroupWithLZ(this, null));
}
}
return ret;
}
/// <summary>
/// 获取当前牌组得牌型
/// </summary>
public CardStyleInfo GetCardStyleInfo()
{
if (this.mLZCards.Count > 0)
{
foreach (var helper in mCardStyleBaseHelpers)
{
if (helper.CheckStyleWithLZ(this, out var chekStyleInfo))
{
return chekStyleInfo;
}
}
}
else
{
foreach (var helper in mCardStyleBaseHelpers)
{
if (helper.CheckStyleNormal(this, out var chekStyleInfo))
{
return chekStyleInfo;
}
}
}
CardStyleInfo cardStyleInfo = new CardStyleInfo();
cardStyleInfo.CardStyle = DataCardConst.CardStyle0;
return cardStyleInfo;
}
/// <summary>
/// 获取当前牌组得所有符合条件牌型
/// </summary>
public List<CardStyleInfo> GetCardStyleInfos()
{
List<CardStyleInfo> cardStyleInfos = new List<CardStyleInfo>();
if (this.mLZCards.Count > 0)
{
foreach (var helper in mCardStyleBaseHelpers)
{
if (helper.CheckStyleWithLZ(this, out var chekStyleInfo))
{
cardStyleInfos.Add(chekStyleInfo);
}
}
}
else
{
foreach (var helper in mCardStyleBaseHelpers)
{
if (helper.CheckStyleNormal(this, out var chekStyleInfo))
{
cardStyleInfos.Add(chekStyleInfo);
}
}
}
return cardStyleInfos;
}
public bool CheckCardStyleInfoValidate(CardStyleInfo cardStyleInfo, out CardStyleInfo chkStyleInfo)
{
chkStyleInfo = new CardStyleInfo();
foreach (var helper in mCardStyleBaseHelpers)
{
if (helper.CardStyle == cardStyleInfo.CardStyle)
{
// 先检测是否符合普通牌型
if (helper.CheckStyleNormal(this, out chkStyleInfo))
{
return true;
}
if (this.mLZCards.Count > 0)
{
return helper.CheckStyleWithLZ(this, out chkStyleInfo);
}
}
}
return false;
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 一个牌型的列表
/// </summary>
public class CardGroupList : List<Card>
{
public CardStyleInfo CardStyleInfo { get; set; }
public CardGroupList()
{
}
public CardGroupList(IEnumerable<Card> cards) : base(cards)
{
}
}
}

View File

@ -0,0 +1,219 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 手牌组
/// 有些逻辑需要校验手牌,用这个组
/// 目前不包含癞子逻辑得判断
/// </summary>
public class CardHandGroup : CardBaseGroup
{
public CardBaseGroup CheckCardGroup { get; private set;}
public CardHandGroup(IEnumerable<Card> cards, Deck deck) : base(cards, deck)
{
}
/// <summary>
/// 拆解部分牌组
/// </summary>
public List<CardGroupList> DecomposePart(List<int> styles)
{
List<CardGroupList> ret = new List<CardGroupList>();
var cardGroup = mDeck.CreateCardGroup(mCards);
foreach (var helper in mCardStyleBaseHelpers)
{
if (!styles.Contains(helper.CardStyle)) continue;
var styleCards = helper.GetAllStyleCards(cardGroup, false);
foreach (var cardList in styleCards)
{
if (cardList.CardStyleInfo == null)
cardList.CardStyleInfo = GetCardStyleInfo(cardList);
}
ret.AddRange(styleCards);
cardGroup.RemoveCards(styleCards);
}
return ret;
}
/// <summary>
/// 拆解牌组
/// </summary>
public List<CardGroupList> Decompose()
{
List<CardGroupList> ret = new List<CardGroupList>();
var cardGroup = mDeck.CreateCardGroup(mCards);
foreach (var helper in mCardStyleBaseHelpers)
{
var styleCards = helper.GetAllStyleCards(cardGroup, false);
foreach (var cardList in styleCards)
{
if (cardList.CardStyleInfo == null)
cardList.CardStyleInfo = GetCardStyleInfo(cardList);
}
ret.AddRange(styleCards);
cardGroup.RemoveCards(styleCards);
}
if (cardGroup.Cards.Count > 0)
{ // 还有未知牌型没有拆解完? (理论不存在)
ret.Add(new CardGroupList(cardGroup.Cards));
}
return ret;
}
public List<CardGroupList> GetShunTip(CardStyleInfo targetStyle, byte[] tipHelpers)
{
List<CardGroupList> ret = new List<CardGroupList>();
for (int i = 0; i < tipHelpers.Length; i++)
{
var helper = GetHelper(tipHelpers[i]);
var tipCardList = helper?.GetAllEatTipGroup(this, targetStyle);
if (tipCardList != null && tipCardList.Count > 0)
{
ret.AddRange(tipCardList.OrderByDescending(x=>x.Count));
return ret;
}
}
return ret;
}
public List<CardGroupList> GetEatTip(int styleId)
{
List<CardGroupList> ret = new List<CardGroupList>();
var helper = GetHelper(styleId);
if (helper != null)
{
ret.AddRange(helper.GetAllEatTipGroup(this, null));
if (ret.Count == 0 && mLZCards.Count > 0)
{
ret.AddRange(helper.GetAllEatTipGroupWithLZ(this, null));
}
}
return ret;
}
/// <summary>
/// 获取吃牌提示
/// </summary>
public List<CardGroupList> GetEatTip(CardStyleInfo targetStyle)
{
List<CardGroupList> ret = new List<CardGroupList>();
if (targetStyle == null)
{
// 没有提供对方牌型,先判断自己手牌是否符合一种牌型可以一次性出完
var styleInfo = GetCardStyleInfo(mCards);
bool isCanOutImmediate = false;
if (styleInfo.CardStyle != DataCardConst.CardStyle0)
{
var cardGroupList = new CardGroupList(mCards);
cardGroupList.CardStyleInfo = styleInfo;
ret.Add(cardGroupList);
isCanOutImmediate = true;
}
if (!isCanOutImmediate)
{
foreach (var helper in mCardStyleBaseHelpers)
{
ret.AddRange(helper.GetAllEatTipGroup(this, null));
}
}
}
else
{
foreach (var helper in mCardStyleBaseHelpers)
{
if (mStyleComparer.Greater(helper.CardStyle, targetStyle.CardStyle))
{
ret.AddRange(helper.GetAllEatTipGroup(this, targetStyle));
}
}
}
// 牌类型赋值
var filterRet = new List<CardGroupList>();
foreach (var cardList in ret)
{
if (cardList.Count == 0) continue;
if (cardList.CardStyleInfo == null)
cardList.CardStyleInfo = GetCardStyleInfo(cardList);
if (mStyleComparer.Greater(cardList.CardStyleInfo, targetStyle))
{
filterRet.Add(cardList);
}
}
return filterRet;
}
/// <summary>
/// 获取cards符合条件牌型
/// </summary>
public CardStyleInfo GetCardStyleInfo(IEnumerable<Card> cards)
{
CheckCardGroup = mDeck.CreateCardGroup(cards);
foreach (var helper in mCardStyleBaseHelpers)
{
if (helper.CheckStyleNormal(this, out var chekStyleInfo))
{
return chekStyleInfo;
}
}
CheckCardGroup = null;
CardStyleInfo cardStyleInfo = new CardStyleInfo();
cardStyleInfo.CardStyle = DataCardConst.CardStyle0;
return cardStyleInfo;
}
/// <summary>
/// 获取cards符合条件牌型
/// </summary>
public List<CardStyleInfo> GetCardStyleInfos(IEnumerable<Card> cards)
{
CheckCardGroup = mDeck.CreateCardGroup(cards);
List<CardStyleInfo> cardStyleInfos = new List<CardStyleInfo>();
foreach (var helper in mCardStyleBaseHelpers)
{
if (helper.CheckStyleNormal(this, out var chekStyleInfo))
{
cardStyleInfos.Add(chekStyleInfo);
}
}
CheckCardGroup = null;
return cardStyleInfos;
}
/// <summary>
/// 检测cards牌型是否符合条件
/// </summary>
public bool CheckCardStyleInfoValidate(IEnumerable<Card> cards, CardStyleInfo cardStyleInfo, out CardStyleInfo chkStyleInfo)
{
chkStyleInfo = new CardStyleInfo();
CheckCardGroup = mDeck.CreateCardGroup(cards);
foreach (var helper in mCardStyleBaseHelpers)
{
if (helper.CardStyle == cardStyleInfo.CardStyle)
{
// 先检测是否符合普通牌型
if (helper.CheckStyleNormal(this, out chkStyleInfo))
{
return true;
}
}
}
return false;
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace GameFix.Poker
{
public class CardIdJsonConverter : JsonConverter
{
ICardIdResolver mResolver;
public CardIdJsonConverter(ICardIdResolver resolver)
{
mResolver = resolver;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is IList<Card> cards)
{
writer.WriteStartArray();
foreach (var card in cards)
{
if (card != null)
writer.WriteValue(card.Id + 1);
else
{
writer.WriteValue(0);
}
}
writer.WriteEndArray();;
} else if (value is Card card)
{
writer.WriteValue(card.Id + 1);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (reader.TokenType == JsonToken.StartArray)
{
List<Card> cards = new List<Card>();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
{
break;
}
if (reader.TokenType == JsonToken.Integer)
{
int id = Convert.ToInt32(reader.Value); // 读取 ID 值
cards.Add(mResolver.GetById(id - 1));
}
}
return cards;
}
if (reader.TokenType == JsonToken.Integer)
{
int id = Convert.ToInt32(reader.Value); // 读取 ID 值
return mResolver.GetById(id - 1);
}
return null;
}
public override bool CanConvert(Type objectType)
{
return typeof(IList<Card>).IsAssignableFrom(objectType) || typeof(Card).IsAssignableFrom(objectType);
}
}
}

View File

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public static class CardListExtensions
{
public static void Sort(this List<Card> cards, CardStyleInfo cardStyleInfo)
{
if (cardStyleInfo == null) return;
if (cardStyleInfo.CardStyle == DataCardConst.CardStyle331
|| cardStyleInfo.CardStyle == DataCardConst.CardStyle332
|| cardStyleInfo.CardStyle == DataCardConst.CardStyle3311)
{
var sortCards = cards.GroupBy(c => c.Value)
.OrderBy(x => x.Key).Select(g => g.ToList()).ToList();
var tmpCards = new List<Card>();
cards.Clear();
for (int i = 0; i < sortCards.Count; i++)
{
if (sortCards[i].Count >= 3)
{
cards.AddRange(sortCards[i].GetRange(0, 3));
sortCards[i].RemoveRange(0,3);
}
tmpCards.AddRange(sortCards[i]);
}
cards.AddRange(tmpCards);
}
else
{
cards.Sort(new CardStyleComparer(cardStyleInfo));
}
}
public static string PrintCards(this IEnumerable<Card> cards)
{
var str = string.Join(",", cards);
return str;
}
#if Server || UNITY_EDITOR
/// <summary>
/// 字符转成Card数组 (主要调试用)
/// </summary>
///
public static List<Card> ToCardList(this string cardString, Deck deck) {
var segment = cardString.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var list = new List<Card>();
foreach (var item in segment)
{
var (suit, value) = CardManager.GetSuitValueFromStr(item);
var card = deck.GetCard(suit, value);
if (card != null)
list.Add(card);
}
return list;
}
#endif
public static void RandomList<T>(this List<T> list)
{
Random rd = new Random();
for (int i = 0; i < list.Count; i++)
{
int idx = rd.Next(0, list.Count);
if (idx != i)
(list[i],list[idx]) = (list[idx], list[i]);
}
}
/// <summary>
/// 判断元素是否都一致
/// </summary>
public static bool AllElementsSame<T>(this IEnumerable<T> enumerable)
{
if (enumerable == null)
throw new ArgumentNullException(nameof(enumerable));
// 如果集合为空,直接返回 true
using (var enumerator = enumerable.GetEnumerator())
{
if (!enumerator.MoveNext())
return true;
// 获取第一个元素作为参照
T first = enumerator.Current;
var comparer = EqualityComparer<T>.Default;
// 遍历后续元素进行比较
while (enumerator.MoveNext())
{
if (!comparer.Equals(first, enumerator.Current))
return false;
}
return true;
}
}
/// <summary>
/// 交换两个列表中的元素(按索引交换)
/// </summary>
/// <typeparam name="T">元素类型</typeparam>
/// <param name="list1">第一个列表</param>
/// <param name="list2">第二个列表</param>
/// <param name="count">交换数量,不传则交换 min(list1.Count, list2.Count)</param>
public static void Switch<T>(this List<T> list1, List<T> list2, int count = -1)
{
if (list1 == null || list2 == null) return;
if (count < 0)
count = Math.Min(list1.Count, list2.Count);
for (int i = 0; i < count && i < list1.Count && i < list2.Count; i++)
{
(list1[i], list2[i]) = (list2[i], list1[i]);
}
}
/// <summary>
/// 交换两个列表中指定的元素
/// </summary>
/// <typeparam name="T">元素类型</typeparam>
/// <param name="list1">第一个列表</param>
/// <param name="list2">第二个列表</param>
/// <param name="items1">要从list1交换出去的元素</param>
/// <param name="items2">要从list2交换出去的元素可选不传则从list2前部取items1.Count个元素</param>
public static void Switch<T>(this List<T> list1, List<T> list2, List<T> items1, List<T> items2 = null)
{
if (list1 == null || list2 == null) return;
if (items1 == null) return;
// 如果items2为空从list2前部取与items1数量相同的元素
if (items2 == null)
{
int count = Math.Min(items1.Count, list2.Count);
items2 = list2.GetRange(0, count);
}
// 从list1移除items1添加items2
foreach (var item in items1)
{
list1.Remove(item);
}
list1.AddRange(items2);
// 从list2移除items2添加items1
foreach (var item in items2)
{
list2.Remove(item);
}
list2.AddRange(items1);
}
}
}

View File

@ -0,0 +1,38 @@
#if Server
using MessagePack;
using MessagePack.Formatters;
namespace GameFix.Poker
{
public class CardMessagePackFormatter : IMessagePackFormatter<Card>
{
ICardIdResolver mResolver;
public CardMessagePackFormatter(ICardIdResolver resolver)
{
mResolver = resolver;
}
public void Serialize(ref MessagePackWriter writer, Card value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}
writer.WriteInt32(value.Id);
}
public Card Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
return mResolver.GetById(reader.ReadInt32());
}
}
}
#endif

View File

@ -0,0 +1,202 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public abstract class CardStyleBaseHelper
{
public virtual int CardStyle => DataCardConst.CardStyle0;
public virtual int CardCnt => 0;
protected readonly int MaxShunLogicNum = 14; // 顺子所能到达得最大值
// 检测普通牌型
internal abstract bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo);
// 检测癞子牌型 (有癞子的检测方法)
internal virtual bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
return CheckStyleNormal(group, out cardStyleInfo);
}
// 获取group中所有符合牌型的组合
internal abstract List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo);
// 获取group中所有符合牌型的组合(带癞子的手牌)
internal virtual List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
return GetAllEatTipGroup(group, eatStyleInfo);
}
/// <summary>
/// 获取所有符合条件得牌型
/// </summary>
internal virtual List<CardGroupList> GetAllStyleCards(CardBaseGroup group, bool isSplitCard) { return new List<CardGroupList>(); }
// 是否都相同
protected bool IsAllSame(List<Card> cards)
{
if (cards.Count == 0) return false;
if (cards.Count == 1) return true;
for (int i = 1; i < cards.Count; i++)
{
if (cards[i].Value != cards[i-1].Value)
{
return false;
}
}
return true;
}
/// <summary>
/// 是否连续
/// </summary>
protected bool IsContinuous(List<int> continuousValues)
{
if (continuousValues.Count == 1) return false;
continuousValues.Sort();
for (int i = 1; i < continuousValues.Count; i++)
{
if (continuousValues[i] - continuousValues[i-1] != 1)
{
return false;
}
}
return true;
}
// 是否连续
protected bool IsContinuous(List<CardGroupList> cardValueCnt, int sameCnt, out List<int> shunValueList)
{
shunValueList = new List<int>();
for (int i = 0; i < cardValueCnt.Count; i++)
{
//顺子中断
if (cardValueCnt[i].Count < 1 && shunValueList.Count > 0)
break;
if (cardValueCnt[i].Count > sameCnt)
break;
if (cardValueCnt[i].Count == sameCnt)
{
shunValueList.Add(i);
}
}
return shunValueList.Count > 0;
}
// 是否最大的连续
protected bool IsMaxContinuous(List<CardGroupList> cardValueCnt, int sameCnt, out List<int> shunValueList)
{
List<int> maxShunValueList = new List<int>();
shunValueList = new List<int>();
for (int i = 0; i < cardValueCnt.Count; i++)
{
if (cardValueCnt[i].Count >= sameCnt)
{
shunValueList.Add(i);
}
else if(shunValueList.Count >= maxShunValueList.Count)
{
maxShunValueList.Clear();
maxShunValueList.AddRange(shunValueList);
shunValueList.Clear();
}
}
if (maxShunValueList.Count > shunValueList.Count)
{
shunValueList.Clear();
shunValueList.AddRange(maxShunValueList);
}
return shunValueList.Count > 0;
}
/// <summary>
/// 判断带癞子的最大连顺
/// </summary>
protected bool IsMaxContinuousByLZ(List<CardGroupList> cardValueCnt, int sameCnt, int lzCount,
out List<int> shunValueList, out List<int> lackValueList)
{
shunValueList = new List<int>();
lackValueList = new List<int>(lzCount);
List<int> maxShunCards = new List<int>();
List<int> maxLackValues = new List<int>(lzCount);
int tmpLzCount = lzCount;
for (int i = 0; i <= MaxShunLogicNum; i++)
{
int count = cardValueCnt[i].Count;
int needCnt = sameCnt - count;
if (cardValueCnt[i].Count == sameCnt)
{
shunValueList.Add(i);
} else if (needCnt <= tmpLzCount
&& needCnt >= 0
&& (shunValueList.Count > 0 || count > 0))
{ // 用癞子补充
tmpLzCount -= needCnt;
shunValueList.Add(i);
lackValueList.AddRange(Enumerable.Repeat(i, needCnt));
}
else
{ // 中断(记录这一次得连顺)
if (shunValueList.Count > maxShunCards.Count)
{
maxShunCards.Clear();
maxShunCards.AddRange(shunValueList);
maxLackValues.Clear();
maxLackValues.AddRange(lackValueList);
}
// 重置癞子
tmpLzCount = lzCount;
shunValueList.Clear();
lackValueList.Clear();
}
}
if (maxShunCards.Count > shunValueList.Count)
{
shunValueList.Clear();
shunValueList.AddRange(maxShunCards);
lackValueList.Clear();
lackValueList.AddRange(maxLackValues);
}
shunValueList.Sort();
return shunValueList.Count > 0;
}
// 相同牌组的值是否更大
protected bool IsSameGroupGreater(CardGroupList group, CardStyleInfo eatStyleInfo)
{
if (group.Count == 0) return false;
if (eatStyleInfo != null && CardStyle == eatStyleInfo.CardStyle)
return group[0].Value > eatStyleInfo.CardValue;
return true;
}
protected bool IsJoker(CardGroupList group)
{
return IsContainJoker(group);
}
protected bool IsContainJoker(List<Card> cards)
{
for (int i = 0; i < cards.Count(); i++)
{
if (DataCardConst.JokerValues.Contains(cards[i].Value))
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,31 @@
using System.Collections.Generic;
using GameMessage;
using System.Linq;
namespace GameFix.Poker
{
public static class CardStyleHelper
{
public static CardStyleInfo CreateCardStyleInfo(int cardStyle,List<Card> cards)
{
CardStyleInfo cardStyleInfo = new CardStyleInfo();
cardStyleInfo.CardStyle = cardStyle;
cardStyleInfo.CardValue = cards.First().Value;
cardStyleInfo.CardCnt = cards.Count;
return cardStyleInfo;
}
public static CardStyleInfoExtend CreateCardStyleInfoExtend(int cardStyle, List<Card> cards)
{
CardStyleInfoExtend cardStyleInfo = new CardStyleInfoExtend();
cardStyleInfo.CardStyle = cardStyle;
cardStyleInfo.CardValue = cards.First().Value;
cardStyleInfo.CardCnt = cards.Count;
cardStyleInfo.SetCardId(cards.First().Id);
return cardStyleInfo;
}
}
}

View File

@ -0,0 +1,17 @@
using GameMessage;
namespace GameFix.Poker
{
public static class CardStyleInfoExt
{
public static string PrintStyleText(this CardStyleInfo cardStyleInfo)
{
if (DataCardConst.CardStyleReadableText.TryGetValue(cardStyleInfo.CardStyle, out string cardStyleText))
{
return $"{cardStyleText}_{cardStyleInfo.CardValue}_{cardStyleInfo.CardCnt}";
}
return $"{cardStyleInfo.CardStyle}_{cardStyleInfo.CardValue}_{cardStyleInfo.CardCnt}";
}
}
}

View File

@ -0,0 +1,254 @@
using System.Collections.Generic;
using System.Linq;
namespace GameFix.Poker
{
public class DataCardConst
{
public const byte CardTypeNormal = 1; // 普通牌
public const byte CardTypeLZ = 2; // 癞子牌
public const byte CardTypeMain = 1 << 2; // 主牌
public const byte CardTypeSecond = 1 << 3; // 副牌
/// <summary>
/// 黑桃
/// </summary>
public const byte SuitSpade = 1;
/// <summary>
/// 红桃
/// </summary>
public const byte SuitHeart = 2;
/// <summary>
/// 梅花
/// </summary>
public const byte SuitClub = 3;
/// <summary>
/// 方块
/// </summary>
public const byte SuitDiamond = 4;
/// <summary>
/// 王
/// </summary>
public const byte SuitJoker = 5;
/// <summary>
/// 无牌型
/// </summary>
public const byte CardStyle0 = 0;
/// <summary>
/// 单张
/// </summary>
public const byte CardStyle1 = 1;
/// <summary>
/// 对子
/// </summary>
public const byte CardStyle2 = 2;
/// <summary>
/// 三张
/// </summary>
public const byte CardStyle3 = 3;
/// <summary>
/// 炸弹 (四张以上)
/// </summary>
public const byte CardStyleBomb = 4;
/// <summary>
/// 火箭
/// </summary>
public const byte CardStyle5 = 5;
/// <summary>
/// 顺子
/// </summary>
public const byte CardStyle123 = 6;
/// <summary>
/// 双顺 3个起
/// </summary>
public const byte CardStyle112233 = 7;
/// <summary>
/// 三顺
/// </summary>
public const byte CardStyle111222 = 8;
/// <summary>
/// 三带一
/// </summary>
public const byte CardStyle31 = 9;
/// <summary>
/// 三带对
/// </summary>
public const byte CardStyle32 = 10;
/// <summary>
/// 四带两张或者一对
/// </summary>
public const byte CardStyle41 = 11;
/// <summary>
/// 四带两对
/// </summary>
public const byte CardStyle42 = 12;
/// <summary>
/// 飞机带单张
/// </summary>
public const byte CardStyle331 = 13;
/// <summary>
/// 飞机带对子
/// </summary>
public const byte CardStyle332 = 14;
/// <summary>
/// 双顺 2个起
/// </summary>
public const byte CardStyle1122 = 15;
/// <summary>
/// 510K
/// </summary>
public const byte CardStyle510K = 16;
/// <summary>
/// 正510K
/// </summary>
public const byte CardStyle510KPlus = 17;
/// <summary>
/// 三带两张
/// </summary>
public const byte CardStyle311 = 18;
/// <summary>
/// 飞机带两张
/// </summary>
public const byte CardStyle3311 = 19;
/// <summary>
/// 带王炸弹 (四张以上)
/// </summary>
public const byte CardStyleJokerBomb = 20;
/// <summary>
/// 甩牌牌型
/// </summary>
public const byte CardStylePutCards = 21;
/// <summary>
/// 三顺 (3个起)
/// </summary>
public const byte CardStyle111222333 = 22;
/// <summary>
/// 全王炸弹
/// </summary>
public const byte CardStyleAllJokerBomb = 23;
/// <summary>
/// 连炸 3连以上
/// </summary>
public const byte CardStyleBomb123 = 24;
/// <summary>
/// 连炸 2连以上
/// </summary>
public const byte CardStyleBomb12 = 25;
/// <summary>
/// 单牌炸弹
/// </summary>
public const byte CardStyleBomb1 = 26;
/// <summary>
/// 对子炸弹
/// </summary>
public const byte CardStyleBomb2 = 27;
/// <summary>
/// 多副510K
/// </summary>
public const byte CardStyle510KMult = 28;
#region
public const byte CardValue2 = 16;
public const byte CardValue3 = 3;
public const byte CardValue5 = 5;
public const byte CardValue10 = 10;
public const byte CardValueK = 13;
public const byte CardValueSmallJoker = 18;
public const byte CardValueBigJoker = 19;
#endregion
// 花色
public static readonly byte[] CardSuits = new byte[]
{
SuitSpade, SuitHeart, SuitClub, SuitDiamond,
};
// A-K
public static readonly byte[] CardValues = new byte[]
{
14, 16, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
};
public static readonly string[] CardValueText = new string[]
{
"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"
};
// 小王 大王
public static readonly byte[] JokerValues = new byte[]
{
18, 19
};
public static readonly Dictionary<int, string> CardStyleReadableText = new Dictionary<int, string>()
{
{CardStyle0, "无牌型" },
{CardStyle1, "单张" },
{CardStyle2, "对子" },
{CardStyle3, "三张" },
{CardStyleBomb, "炸弹" },
{CardStyleJokerBomb, "炸弹" },
{CardStyle5, "火箭" },
{CardStyle31, "三带一" },
{CardStyle311, "三带两张" },
{CardStyle32, "三带二" },
{CardStyle41, "四带两张或者一对" },
{CardStyle42, "四带两对" },
{CardStyle331, "飞机带单张" },
{CardStyle3311, "飞机带两张" },
{CardStyle332, "飞机带对子" },
{CardStyle123, "顺子" },
{CardStyle112233, "双顺" },
{CardStyle111222, "三顺" },
{CardStyle1122, "双顺" },
{CardStyle510K, "副510K" },
{CardStyle510KPlus, "正510K" },
{CardStylePutCards, "甩牌" },
{CardStyleAllJokerBomb, "王炸" },
{CardStyleBomb1, "单牌炸" },
{CardStyleBomb2, "对子炸" },
{CardStyle510KMult, "多510K"}
};
public static bool IsJoker(Card card)
{
return JokerValues.Contains(card.Value);
}
}
}

145
GameFix/Common/Card/Deck.cs Normal file
View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace GameFix.Poker
{
/// <summary>
/// 桌面牌组管理
/// </summary>
public class Deck : List<Card>, ICardIdResolver
{
public static readonly Random Random = new Random();
public new Card this[int index] => (Card)base[index];
public IStyleComparer StyleComparer => mBaseLogic.GetStyleComparer();
public DeckSerializer Serializer {get; private set;}
private DeckBaseLogic mBaseLogic;
internal Deck(DeckBaseLogic baseLogic) : base(baseLogic.Create())
{
mBaseLogic = baseLogic;
Serializer = new DeckSerializer(this);
}
public T GetDeckLogic<T>()
{
if (mBaseLogic is T logic)
return logic;
return default;
}
public IEnumerable<Card> GetCards(Func<Card, bool> predicate)
{
return this.Where(predicate);
}
public List<Card> GetCards(IEnumerable<int> ids)
{
List<Card> cards = new List<Card>();
var enumerable = ids.ToList();
for (int i = 0; i < enumerable.Count(); i++)
{
var card = GetById(enumerable.ElementAt(i));
if (card != null)
cards.Add(card);
}
return cards;
}
public Card GetCard(byte suit, int value)
{
return GetCards(p=>p.Suit == suit && p.Value == value).FirstOrDefault();
}
public Card GetCard(byte suit, int value, Func<Card, bool> condition)
{
return GetCards(p=>p.Suit == suit && p.Value == value)
.Where(condition).FirstOrDefault();
}
public Card GetById(int id)
{
if (id < 0 || id >= Count) return null;
return this[id];
}
public void RevertAllCardType()
{
for (int i = 0; i < this.Count; i++)
{
this[i].CardType = DataCardConst.CardTypeNormal;
}
}
/// <summary>
/// 刷新牌类型
/// </summary>
public void RefreshCardType(byte cardType, List<Card> cards)
{
RevertAllCardType();
for (int i = 0; i < cards.Count; i++)
{
int index = this.IndexOf(cards[i]);
if (index >= 0)
this[index].CardType = cardType;
}
}
public void RefreshCardType(byte cardType, List<int> ids)
{
RevertAllCardType();
for (int i = 0; i < ids.Count; i++)
{
var card = GetById(ids[i]);
if (card != null)
card.CardType = cardType;
}
}
public void RefreshCardType(byte cardType, List<byte> values)
{
RevertAllCardType();
for (int i = 0; i < this.Count; i++)
{
if (values.Contains(this[i].Value))
this[i].CardType = cardType;
}
}
/// <summary>
/// 将牌列表封装成牌组,可用于牌型等判断
/// </summary>
public CardGroup CreateCardGroup(IEnumerable<Card> cards)
{
var cardGroup = new CardGroup(cards, this);
var styleHelpers = mBaseLogic.GetAllStyleHelper(cardGroup);
var styleComparer = mBaseLogic.GetStyleComparer();
cardGroup.RegisterHelper(styleHelpers);
cardGroup.RegisterStyleComparer(styleComparer);
return cardGroup;
}
public CardHandGroup CreateCardHandGroup(IEnumerable<Card> cards)
{
var cardGroup = new CardHandGroup(cards, this);
var styleHelpers = mBaseLogic.GetAllStyleHelper(cardGroup);
var styleComparer = mBaseLogic.GetStyleComparer();
cardGroup.RegisterHelper(styleHelpers);
cardGroup.RegisterStyleComparer(styleComparer);
return cardGroup;
}
public static Deck Create(DeckBaseLogic baseLogic)
{
var deck = new Deck(baseLogic);
baseLogic.SetDeck(deck);
baseLogic.OnInit();
return deck;
}
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
namespace GameFix.Poker
{
public abstract class DeckBaseLogic
{
protected Deck mDeck;
public void SetDeck(Deck deck)
{
mDeck = deck;
}
/// <summary>
/// 初始化方法
/// </summary>
public virtual void OnInit() { }
// 创建牌组
public abstract List<Card> Create();
// 获取牌型检测方法
public abstract List<CardStyleBaseHelper> GetAllStyleHelper(CardBaseGroup cardGroup);
// 获取牌型比较器
public abstract IStyleComparer GetStyleComparer();
}
}

View File

@ -0,0 +1,77 @@
#if Server
using System;
using System.Text;
using MessagePack;
using MessagePack.Formatters;
using MessagePack.Resolvers;
using Newtonsoft.Json;
namespace GameFix.Poker
{
public class DeckSerializer
{
private Deck mDeck;
private MessagePackSerializerOptions mOptions;
private JsonSerializerSettings mMemSaveJsonSet;
public DeckSerializer(Deck deck)
{
mDeck = deck;
// MessagePack
var resolver = CompositeResolver.Create(
new IMessagePackFormatter[]
{
new CardMessagePackFormatter(mDeck),
},
new IFormatterResolver[]
{
StandardResolver.Instance
}
);
mOptions = MessagePackSerializerOptions.Standard
.WithResolver(resolver);
// .WithCompression(MessagePackCompression.Lz4BlockArray);
// Json
mMemSaveJsonSet = new JsonSerializerSettings();
mMemSaveJsonSet.DefaultValueHandling = DefaultValueHandling.Ignore;
mMemSaveJsonSet.Converters = new JsonConverter[] { new CardIdJsonConverter(mDeck) };
}
public byte[] Serialize<T>(T value, bool isJson = false)
{
if (isJson)
{
string jsonData = JsonHelper.SerializeObject(value, mMemSaveJsonSet);
return Encoding.UTF8.GetBytes(jsonData);
}
return MessagePackSerializer.Serialize(value, mOptions);
}
public T Deserialize<T>(byte[] data, bool isJson = false)
{
if (isJson)
{
string jsondata = Encoding.UTF8.GetString(data);
return JsonHelper.DeserializeObject<T>(jsondata, mMemSaveJsonSet);
}
return MessagePackSerializer.Deserialize<T>(data, mOptions);
}
}
}
#else
namespace GameFix.Poker
{
public class DeckSerializer
{
private Deck mDeck;
public DeckSerializer(Deck deck)
{
mDeck = deck;
}
}
}
#endif

View File

@ -0,0 +1,7 @@
namespace GameFix.Poker
{
public interface ICardIdResolver
{
Card GetById(int id);
}
}

View File

@ -0,0 +1,12 @@
using GameMessage;
namespace GameFix.Poker
{
public interface IStyleComparer
{
// 比较两个牌型类 (大于等于)
bool Greater(CardStyleInfo a, CardStyleInfo b);
// 比较两个牌型id (大于等于)
bool Greater(int a, int b);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cce19ec85cde409c8ba213a878216016
timeCreated: 1741678598

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Linq;
namespace GameFix.Poker
{
/// <summary>
/// 三顺 (3个起)
/// </summary>
public class CardStyle111222333Helper : CardStyle111222Helper
{
public override int CardStyle => DataCardConst.CardStyle111222333;
public override int CardCnt => 9;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a48ff248eb6640c9817ac501c87c11cb
timeCreated: 1748935610

View File

@ -0,0 +1,88 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle111222Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle111222;
public override int CardCnt => 6;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt && group.Cards.Count % 3 == 0)
{
if (IsContinuous(group.CardGroupList, 3, out var shunCnt)
&& shunCnt.Count * 3 == group.Cards.Count)
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt && group.Cards.Count % 3 == 0)
{
if (IsAllSame(group.NoLZCards)) return false;
if (IsMaxContinuousByLZ(group.CardGroupList, 3, group.LZCards.Count,
out var shunValueList, out var lackValueList))
{
if (shunValueList.Count * 3 == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * 3);
cardStyleInfo.CardLackValues = lackValueList.ToArray();
return true;
}
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3;
var continuousCnt = eatStyleInfo?.CardCnt ?? -1;
List<Card> continuousList = new List<Card>();
for (int i = minValue; i < group.CardGroupList.Count; i++)
{
continuousList.Clear();
for (int j = 0; j < group.CardGroupList.Count; j++)
{
if (i + j >= group.CardGroupList.Count)
break;
if (group.CardGroupList[i + j].Count <= 2)
break;
continuousList.AddRange(group.CardGroupList[i + j].GetRange(0, 3));
if (continuousList.Count >= CardCnt && continuousList.Count % 3 == 0)
{
if (continuousCnt == -1 || continuousCnt == continuousList.Count)
{
groupList.Add(new CardGroupList(continuousList));
}
}
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{// 这里不处理直接找炸弹
return new List<CardGroupList>();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0c80edd1dc98428c973c5a3a193aa748
timeCreated: 1741684811

View File

@ -0,0 +1,123 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle112233Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle112233;
public override int CardCnt => 6;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt && group.Cards.Count % 2 == 0)
{
if (IsContinuous(group.CardGroupList, 2, out var shunCnt)
&& shunCnt.Count * 2 == group.Cards.Count)
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt && group.Cards.Count % 2 == 0)
{
if (IsAllSame(group.NoLZCards)) return false;
if (IsMaxContinuousByLZ(group.CardGroupList, 2, group.LZCards.Count,
out var shunValueList, out var lackValueList))
{
if (shunValueList.Count * 2 == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * 2);
cardStyleInfo.CardLackValues = lackValueList.ToArray();
return true;
}
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3;
var continuousCnt = eatStyleInfo?.CardCnt ?? -1;
List<Card> continuousList = new List<Card>();
for (int i = minValue; i < group.CardGroupList.Count; i++)
{
continuousList.Clear();
for (int j = 0; j < group.CardGroupList.Count; j++)
{
if (i + j >= group.CardGroupList.Count)
break;
if (group.CardGroupList[i + j].Count <= 1)
break;
continuousList.AddRange(group.CardGroupList[i + j].GetRange(0, 2));
if (continuousList.Count >= CardCnt && continuousList.Count % 2 == 0)
{
if (continuousCnt == -1 || continuousCnt == continuousList.Count)
{
groupList.Add(new CardGroupList(continuousList));
}
}
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3;
var continuousCnt = eatStyleInfo?.CardCnt ?? -1;
var groupList = new List<CardGroupList>();
List<Card> continuousList = new List<Card>();
List<int> lackValueList = new List<int>();
int needCnt = 0;
for (int i = minValue; i < group.CardGroupList.Count; i++)
{
var count = group.CardGroupList[i].Count;
if (count >= 2)
{
continuousList.AddRange(group.CardGroupList[i].GetRange(0, 2));
}
else
{
needCnt += (2 - count);
lackValueList.AddRange(Enumerable.Repeat(i, (2 - count)));
}
bool isBreak = group.CardGroupList[i].Count < 2 && needCnt > group.LZCards.Count;
if (continuousList.Count + needCnt >= CardCnt && needCnt <= group.LZCards.Count)
{
if (continuousCnt == -1 || continuousCnt == continuousList.Count + needCnt)
{
continuousList.AddRange(group.LZCards.GetRange(0, needCnt));
groupList.Add(new CardGroupList(continuousList));
break;
}
} else if (isBreak)
{
continuousList.Clear();
needCnt = 0;
}
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8a1287609db44ab2a55656eb154266d4
timeCreated: 1741684811

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Linq;
namespace GameFix.Poker
{
public class CardStyle1122Helper : CardStyle112233Helper
{
public override int CardStyle => DataCardConst.CardStyle1122;
public override int CardCnt => 4;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 57d250baed484c6ca90c588c01bc11f0
timeCreated: 1741763235

View File

@ -0,0 +1,123 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle123Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle123;
public override int CardCnt => 5;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt)
{
if (IsContinuous(group.CardGroupList, 1, out var shunCnt)
&& shunCnt.Count == group.Cards.Count)
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt)
{
if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹
if (IsMaxContinuousByLZ(group.CardGroupList, 1, group.LZCards.Count,
out var shunValueList, out var lackValueList))
{
if (shunValueList.Count == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count);
cardStyleInfo.CardLackValues = lackValueList.ToArray();
return true;
}
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3;
var continuousCnt = eatStyleInfo?.CardCnt ?? -1;
List<Card> continuousList = new List<Card>();
for (int i = minValue; i < group.CardGroupList.Count; i++)
{
continuousList.Clear();
for (int j = 0; j < group.CardGroupList.Count; j++)
{
if (i + j >= group.CardGroupList.Count)
break;
if (group.CardGroupList[i + j].Count == 0)
break;
continuousList.Add(group.CardGroupList[i + j].First());
if (continuousList.Count >= 5)
{
if (continuousCnt == -1 || continuousCnt == continuousList.Count)
{
groupList.Add(new CardGroupList(continuousList));
}
}
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var minValue = eatStyleInfo != null ? eatStyleInfo.CardValue + 1 : 3;
var continuousCnt = eatStyleInfo?.CardCnt ?? -1;
var groupList = new List<CardGroupList>();
List<Card> continuousList = new List<Card>();
List<int> lackValues = new List<int>();
int needCnt = 0;
for (int i = minValue; i < MaxShunLogicNum; i++)
{
if (group.CardGroupList[i].Count > 0)
{
continuousList.Add(group.CardGroupList[i].First());
}
else
{
needCnt++;
lackValues.Add(i);
}
bool isBreak = group.CardGroupList[i].Count < 1 && needCnt > group.LZCards.Count;
if (continuousList.Count + needCnt >= CardCnt && needCnt <= group.LZCards.Count)
{
if (continuousCnt == -1 || continuousCnt == continuousList.Count + needCnt)
{
continuousList.AddRange(group.LZCards.GetRange(0, needCnt));
groupList.Add(new CardGroupList(continuousList));
break;
}
} else if (isBreak)
{
lackValues.Clear();
continuousList.Clear();
needCnt = 0;
}
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7b1e3edc09a540b587d0f0b36cbf4f2d
timeCreated: 1741684811

View File

@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle1Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle1;
public override int CardCnt => 1;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count != CardCnt) return false;
// 单张癞子只能当普通牌
if (group.LZCards.Count == group.Cards.Count)
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards);
return true;
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
for (int i = 0; i < groupCnt; i++)
{
var cardGroup = group.CardGroupList[i];
if (cardGroup.Count >= CardCnt && cardGroup.Count < 4 && IsSameGroupGreater(cardGroup, eatStyleInfo))
{
groupList.Add(new CardGroupList(cardGroup));
}
}
// 从单牌到三张排序,优先不拆组合牌
if (groupList.Count > 0)
{
groupList = groupList.OrderBy(x=>x.Count).ToList();
groupList.ForEach(x => x.RemoveRange(1, x.Count - 1));
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
// 在常规情况下没有找到单牌可以出 (说明只剩下癞子)
// 单牌也只能按原始牌值出
var groupList = new List<CardGroupList>(group.LZCards.Count);
for (int i = 0; i < group.LZCards.Count; i++)
{
var lzCard = group.LZCards[i];
if (lzCard.Value > eatStyleInfo.CardValue)
{
var lzCards = new CardGroupList();
lzCards.Add(lzCard);
groupList.Add(lzCards);
}
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1a22e938dae646c8bf2d19ddf7fc9456
timeCreated: 1741678606

View File

@ -0,0 +1,98 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle2Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle2;
public override int CardCnt => 2;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt && IsAllSame(group.Cards))
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count != CardCnt) return false;
if (IsContainJoker(group.NoLZCards)) return false;
if (group.LZCards.Count == group.Cards.Count && IsAllSame(group.LZCards))
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards);
return true;
}
if (IsAllSame(group.NoLZCards))
{
var cardV = group.NoLZCards[0].Value;
cardStyleInfo = new CardStyleInfo(CardStyle, cardV, CardCnt);
cardStyleInfo.CardLackValues = Enumerable.Repeat((int)cardV, group.LZCards.Count).ToArray();
return true;
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
for (int i = 0; i < groupCnt; i++)
{
var cardGroup = group.CardGroupList[i];
if (cardGroup.Count >= CardCnt && cardGroup.Count < 4 && IsSameGroupGreater(cardGroup, eatStyleInfo))
{
groupList.Add(new CardGroupList(cardGroup));
}
}
// 从两张牌到三张排序,优先不拆组合牌
if (groupList.Count > 0)
{
groupList = groupList.OrderBy(x=>x.Count).ToList();
groupList.ForEach(x => x.RemoveRange(2, x.Count - 2));
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1).SelectMany(kv => kv.Value).ToList();
var groupList = new List<CardGroupList>();
foreach (var cardGroup in cardGroupList)
{
if (cardGroup.Count > 0
&& cardGroup.First().Value > eatStyleInfo.CardValue
&& !IsJoker(cardGroup))
{
List<Card> tmpList = new List<Card>();
tmpList.Add(cardGroup.First());
tmpList.Add(group.LZCards.First());
groupList.Add(new CardGroupList(tmpList));
}
}
if (groupList.Count == 0
&& group.LZCards.Count >= CardCnt
&& IsAllSame(group.LZCards)
&& group.LZCards.First().Value > eatStyleInfo.CardValue)
{
var cardList = group.LZCards.GetRange(0, CardCnt);
groupList.Add(new CardGroupList(cardList));
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cbbf72f272f140c2bf88d78ca167115b
timeCreated: 1741678606

View File

@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle311Helper : CardStyle31Helper
{
public override int CardStyle => DataCardConst.CardStyle311;
public override int CardCnt => 5;
// 4个相同得带一个是否能够出
private bool _isSame4Enable = true;
public CardStyle311Helper(){}
public CardStyle311Helper(bool isSame4Enable){ _isSame4Enable = isSame4Enable; }
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
for (int i = 0; i < group.CardGroupList.Count; i++)
{
if (group.CardGroupList[i].Count == 3 || (_isSame4Enable && group.CardGroupList[i].Count == 4))
{
cardStyleInfo = new CardStyleInfo(CardStyle, i, group.Cards.Count);
return true;
}
}
}
return false;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f30f25213583439d8ee4b70ecc0b0a32
timeCreated: 1741833322

View File

@ -0,0 +1,46 @@
using System.Collections.Generic;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 3带2张 特殊牌型
/// 最后一手牌可以带一张或者不带
/// </summary>
public class CardStyle311SpecialHelper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle311;
public override int CardCnt => 5;
private readonly CardStyle311Helper _cardStyle311 = new();
private readonly CardStyle31Helper _cardStyle31 = new();
private readonly CardStyle3Helper _cardStyle3 = new();
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group is not CardHandGroup) return false;
bool isStyle = false;
if (group.SelfCards.Count == _cardStyle3.CardCnt)
isStyle = _cardStyle3.CheckStyleNormal(group, out cardStyleInfo);
else if (group.SelfCards.Count == _cardStyle31.CardCnt)
isStyle = _cardStyle31.CheckStyleNormal(group, out cardStyleInfo);
else
isStyle = _cardStyle311.CheckStyleNormal(group, out cardStyleInfo);
if (isStyle)
cardStyleInfo.CardStyle = CardStyle;
return isStyle;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
if (group is not CardHandGroup) return new List<CardGroupList>();
if (group.SelfCards.Count == _cardStyle3.CardCnt)
return _cardStyle3.GetAllEatTipGroup(group, eatStyleInfo);
if (group.SelfCards.Count == _cardStyle31.CardCnt)
return _cardStyle31.GetAllEatTipGroup(group, eatStyleInfo);
var allEatTipGroup = _cardStyle311.GetAllEatTipGroup(group, eatStyleInfo);
return allEatTipGroup;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dd25a8723adf4b9cb658aef67a327ea2
timeCreated: 1779153231

View File

@ -0,0 +1,127 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle31Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle31;
public override int CardCnt => 4;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
for (int i = 0; i < group.CardGroupList.Count; i++)
{
if (group.CardGroupList[i].Count == 3)
{
cardStyleInfo = new CardStyleInfo(CardStyle, i, group.Cards.Count);
return true;
}
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹
int maxCV = 0;
for (int i = 0; i < group.CardGroupList.Count; i++)
{
if (group.CardGroupList[i].Count + group.LZCards.Count == 3 && i>maxCV)
{
maxCV = i;
}
}
if (maxCV != 0)
{
cardStyleInfo = new CardStyleInfo(CardStyle, maxCV, CardCnt);
cardStyleInfo.CardLackValues = Enumerable.Repeat(maxCV, group.LZCards.Count).ToArray();
return true;
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
var sortGroupList = group.CardGroupList.OrderBy((x) => x.Count).ToList();
for (int i = 0; i < groupCnt; i++)
{
var cardGroup = group.CardGroupList[i];
if (cardGroup.Count == 3 && IsSameGroupGreater(cardGroup, eatStyleInfo))
{
// 从其他组中找牌
var newCardGroup = new CardGroupList(cardGroup);
for (int j = 0; j < groupCnt; j++)
{
if (sortGroupList[j] != cardGroup && sortGroupList[j].Count >= 1)
{
int needCnt = CardCnt - newCardGroup.Count;
needCnt = needCnt > sortGroupList[j].Count ? sortGroupList[j].Count : needCnt;
newCardGroup.AddRange(sortGroupList[j].GetRange(0, needCnt));
if (newCardGroup.Count == CardCnt)
{
groupList.Add(newCardGroup);
break;
}
}
}
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1)
.SelectMany(kv => kv.Value).OrderByDescending(x=>x.Count).ToList();
var groupList = new List<CardGroupList>();
foreach (var cardGroup in cardGroupList)
{
if (cardGroup.Count > 0
&& cardGroup.First().Value > eatStyleInfo.CardValue
&& !IsJoker(cardGroup))
{
var needCnt = CardCnt - cardGroup.Count - 1;
if (needCnt <= group.LZCards.Count)
{
List<Card> tmpList = new List<Card>();
tmpList.AddRange(cardGroup);
tmpList.AddRange(group.LZCards.GetRange(0, needCnt));
for (int i = cardGroupList.Count - 1; i >= 0; i--)
{
if (cardGroupList[i] != cardGroup)
{
int wingNeedCnt = CardCnt - tmpList.Count;
wingNeedCnt = wingNeedCnt > cardGroupList[i].Count ? cardGroupList[i].Count : wingNeedCnt;
tmpList.AddRange(cardGroupList[i].GetRange(0, wingNeedCnt));
break;
}
}
if (tmpList.Count == CardCnt)
{
groupList.Add(new CardGroupList(tmpList));
}
}
}
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d6a6678f5c6491fb155a6900558fc47
timeCreated: 1741684811

View File

@ -0,0 +1,156 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle32Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle32;
public override int CardCnt => 5;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
int mainValue = 0, withValue = 0;
for (int i = 0; i < group.CardGroupList.Count; i++)
{
if (group.CardGroupList[i].Count == 3)
{
mainValue = i;
}
if (group.CardGroupList[i].Count == 2)
{
withValue = i;
}
}
if (mainValue > 0 && withValue > 0)
{
cardStyleInfo = new CardStyleInfo(CardStyle, mainValue, group.Cards.Count);
return true;
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹
int lzCount = group.LZCards.Count;
int threeCV = 0, pairCV = 0;
List<int> lackValues = new List<int>(group.LZCards.Count);
for (int i = group.CardGroupList.Count - 1; i >= 0; i--)
{
int cardV = i;
int count = group.CardGroupList[i].Count;
if (count == 0) continue;
int needCnt = 0;
if ((3 - count) <= lzCount)
{
// 找到三张相同的牌
needCnt = 3 - count;
threeCV = cardV;
}
else if ((2 - count) <= lzCount)
{
// 找到两张相同的牌
needCnt = 2 - count;
pairCV = cardV;
}
lzCount -= needCnt;
lackValues.AddRange(Enumerable.Repeat(cardV, needCnt));
}
if (threeCV != 0 && pairCV != 0 && lzCount == 0)
{
cardStyleInfo = new CardStyleInfo(CardStyle, threeCV, CardCnt);
cardStyleInfo.CardLackValues = lackValues.ToArray();
return true;
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
var sortGroupList = group.CardGroupList.OrderBy((x) => x.Count).ToList();
for (int i = 0; i < groupCnt; i++)
{
var cardGroup = group.CardGroupList[i];
if (cardGroup.Count == 3 && IsSameGroupGreater(cardGroup, eatStyleInfo))
{
// 从其他组中找一对
for (int j = 0; j < groupCnt; j++)
{
if (sortGroupList[j] != cardGroup && sortGroupList[j].Count >= 2)
{
var newCardGroup = new CardGroupList(cardGroup);
newCardGroup.AddRange(sortGroupList[j].GetRange(0, 2));
groupList.Add(newCardGroup);
}
}
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1)
.SelectMany(kv => kv.Value).OrderByDescending(x=>x.Count).ToList();
var groupList = new List<CardGroupList>();
foreach (var cardGroup in cardGroupList)
{
if (cardGroup.Count > 0
&& cardGroup.First().Value > eatStyleInfo.CardValue
&& !IsJoker(cardGroup))
{
var needCnt = CardCnt - cardGroup.Count - 2;
if (needCnt <= group.LZCards.Count)
{
List<Card> tmpList = new List<Card>();
tmpList.AddRange(cardGroup);
// 获取一个对子,不够用癞子补充
for (int i = cardGroupList.Count - 1; i >= 0; i--)
{
if (cardGroupList[i] != cardGroup && !IsJoker(cardGroupList[i]))
{
if (cardGroupList[i].Count >= 2)
{
tmpList.AddRange(cardGroupList[i].GetRange(0, 2));
break;
}
else if (2 - cardGroupList[i].Count <= group.LZCards.Count - needCnt)
{
tmpList.AddRange(cardGroupList[i]);
needCnt += (2 - cardGroupList[i].Count);
break;
}
}
}
tmpList.AddRange(group.LZCards.GetRange(0, needCnt));
if (tmpList.Count == CardCnt)
{
groupList.Add(new CardGroupList(tmpList));
}
}
}
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e0e6ce2f17c14aeeb059d36fd28bdb71
timeCreated: 1741684811

View File

@ -0,0 +1,9 @@
namespace GameFix.Poker
{
public class CardStyle3311Helper : CardStyle331Helper
{
public override int CardStyle => DataCardConst.CardStyle3311;
public override int CardCnt => 10;
protected override int GroupCnt => 5;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b880249d8307412788972a6c10632d88
timeCreated: 1741844953

View File

@ -0,0 +1,71 @@
using System.Collections.Generic;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 飞机 带 4张
/// 最后一手牌可以少带或者不带
/// </summary>
public class CardStyle3311SpecialHelper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle3311;
public override int CardCnt => 10;
private readonly CardStyle3311Helper _cardStyle3311 = new();
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
// 手牌对象才检测
if (group is not CardHandGroup) return false;
// 只要是飞机就可以带剩下得牌
// 先过滤是否看有三个得牌
var cardList = new List<CardGroupList>();
foreach (var cardGroup in group.SelfCardGroupDic)
{
if (cardGroup.Key >= 3)
cardList.AddRange(cardGroup.Value);
}
if (cardList.Count >= 2)
{
if (group.SelfCards.Count == group.Cards.Count)
{
// 剩下得牌都出完
if (IsMaxContinuous(group.CardGroupList, 3, out var shunValueList)
&& shunValueList.Count >= 2
&& shunValueList.Count * 5 >= group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], group.Cards.Count);
return true;
}
return false;
}
return _cardStyle3311.CheckStyleNormal(group, out cardStyleInfo);
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
// 一定要检测手牌
var ret = new List<CardGroupList>();
if (group is not CardHandGroup handGroup) return ret;
if (group.SelfCardGroupDic.TryGetValue(3, out var cardList)
&& cardList.Count >= 2)
{
// 手上得牌是否可以成为飞机
var cardStyle = handGroup.GetCardStyleInfo(group.SelfCards);
if (cardStyle.CardStyle == CardStyle)
{
ret.Add(new CardGroupList(group.SelfCards));
return ret;
}
return _cardStyle3311.GetAllEatTipGroup(group, eatStyleInfo);
}
return ret;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1590d41252e54faa99b790979e1bbef5
timeCreated: 1779156320

View File

@ -0,0 +1,105 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle331Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle331;
public override int CardCnt => 8;
protected virtual int GroupCnt => 4;
// 特殊情况
// 333444 666777888999101010JJJ (理论不存在)
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt && group.Cards.Count % GroupCnt == 0)
{
if (IsMaxContinuous(group.CardGroupList, 3, out var shunValueList)
&& shunValueList.Count * GroupCnt == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], group.Cards.Count);
return true;
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt && group.Cards.Count % GroupCnt == 0)
{
if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹
if (IsMaxContinuousByLZ(group.CardGroupList, 3, group.LZCards.Count,
out var shunValueList, out var lackValueList))
{
if (shunValueList.Count * GroupCnt == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * 4);
cardStyleInfo.CardLackValues = lackValueList.ToArray();
return true;
}
}
}
return false;
}
// 333444555 888
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
var planeHelper = new CardStyle111222Helper();
// 先找到所有的飞机
CardStyleInfo tmpEatStyleInfo = null;
if (eatStyleInfo != null)
tmpEatStyleInfo = new CardStyleInfo(
eatStyleInfo.CardStyle,
eatStyleInfo.CardValue,
eatStyleInfo.CardCnt / GroupCnt * 3);
var allPlaneList = planeHelper.GetAllEatTipGroup(group, tmpEatStyleInfo);
List<Card> wingCards = new List<Card>();
foreach (var plane in allPlaneList)
{
var singleCnt = (plane.Count / 3) * (GroupCnt - 3); // 需要单牌得数量
// 找到符合条件得所有翅膀
var wingGroupList = group.CardGroupDic
.Where(kv => kv.Key >= 1).SelectMany(kv=>kv.Value).ToList();
wingCards.Clear();
foreach (var wingGroup in wingGroupList)
{
if (wingGroup.Count >= 3 && plane.Intersect(wingGroup).Any()) // 翅膀不应该来源于飞机
continue;
// 将翅膀加入列表中
var needCnt = singleCnt - wingCards.Count;
needCnt = needCnt > wingGroup.Count ? wingGroup.Count : needCnt;
wingCards.AddRange(wingGroup.GetRange(0, needCnt));
if (wingCards.Count == singleCnt)
{
plane.AddRange(wingCards);
groupList.Add(new CardGroupList(plane));
break;
}
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{// 这里不处理直接找炸弹
return new List<CardGroupList>();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b2b52aa3f44a4eb792d5f2e8c0137f79
timeCreated: 1741684811

View File

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle332Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle332;
public override int CardCnt => 10;
// 特殊牌型
// 888999 7777
// 666777888999 33334444
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt && group.Cards.Count % 5 == 0)
{
if (IsMaxContinuous(group.CardGroupList, 3, out var shunValueList))
{
// 检测剩下的是否都是都是一对
int doubleNum = 0;
for (int i = 0; i < group.CardGroupList.Count; i++)
{
var cnt = group.CardGroupList[i].Count;
if (shunValueList.Contains(i))
{ // 超出部分可以组成对子
if (cnt <= 3) continue;
if (cnt % 2 == 0)
{ // 拆了这个组合
doubleNum += cnt / 2;
shunValueList.Remove(i);
} else if ((cnt - 3) % 2 == 0)
{
doubleNum += (cnt - 3) / 2;
}
continue;
}
if (group.CardGroupList[i].Count % 2 == 0)
doubleNum += group.CardGroupList[i].Count / 2;
}
if (IsContinuous(shunValueList)
&& doubleNum == shunValueList.Count
&& shunValueList.Count * 5 == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], group.Cards.Count);
return true;
}
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt && group.Cards.Count % 5 == 0)
{
if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹
if (IsMaxContinuousByLZ(group.CardGroupList, 3, group.LZCards.Count,
out var shunValueList, out var lackValueList))
{
// 判断剩下得牌是否都是一对
int doubleNum = 0;
int lzCount = group.LZCards.Count - lackValueList.Count;
for (int i = 0; i < group.CardGroupList.Count; i++)
{
int count = group.CardGroupList[i].Count;
if (shunValueList.Contains(i) || count == 0) continue;
if (count == 2 || ((2 - count) <= lzCount && (2 - count) > 0))
{
doubleNum++;
lzCount -= (2 - count);
shunValueList.AddRange(Enumerable.Repeat(i, (2 - count)));
} else if (count == 4 || ((4 - count) <= lzCount && (4 - count) > 0))
{
doubleNum += 2;
lzCount -= (4 - count);
shunValueList.AddRange(Enumerable.Repeat(i, (4 - count)));
}
}
if (doubleNum == shunValueList.Count
&& shunValueList.Count * 5 == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * 5);
cardStyleInfo.CardLackValues = lackValueList.ToArray();
return true;
}
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
var planeHelper = new CardStyle111222Helper();
// 先找到所有的飞机
CardStyleInfo tmpEatStyleInfo = null;
if (eatStyleInfo != null)
tmpEatStyleInfo = new CardStyleInfo(
eatStyleInfo.CardStyle,
eatStyleInfo.CardValue,
eatStyleInfo.CardCnt / 5 * 3);
var allPlaneList= planeHelper.GetAllEatTipGroup(group, tmpEatStyleInfo);
List<Card> wingCards = new List<Card>();
foreach (var plane in allPlaneList)
{
var doubleCnt = plane.Count / 3; // 需要对子得数量
// 找到符合条件得所有翅膀
var wingGroupList = group.CardGroupDic
.Where(kv => kv.Key >= 2).SelectMany(kv=>kv.Value).ToList();
wingCards.Clear();
foreach (var wingGroup in wingGroupList)
{
if (wingGroup.Count >= 3 && plane.Intersect(wingGroup).Any()) // 翅膀不应该来源于飞机
continue;
// 将翅膀加入列表中
var needCnt = doubleCnt * 2 - wingCards.Count;
needCnt = needCnt > wingGroup.Count ? wingGroup.Count : needCnt;
needCnt = needCnt % 2 == 0 ? needCnt : needCnt - 1; // 不能为奇数
wingCards.AddRange(wingGroup.GetRange(0, needCnt));
if (wingCards.Count == doubleCnt * 2)
{
plane.AddRange(wingCards);
groupList.Add(new CardGroupList(plane));
break;
}
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{// 这里不处理直接找炸弹
return new List<CardGroupList>();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eac176f1d3544cdb9160610fe6353bc1
timeCreated: 1741684811

View File

@ -0,0 +1,96 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle3Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle3;
public override int CardCnt => 3;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt && IsAllSame(group.Cards))
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count != CardCnt) return false;
if (IsContainJoker(group.NoLZCards)) return false;
if (group.LZCards.Count == group.Cards.Count && IsAllSame(group.LZCards))
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards);
return true;
}
if (IsAllSame(group.NoLZCards))
{
var cardV = group.NoLZCards[0].Value;
cardStyleInfo = new CardStyleInfo(CardStyle, cardV, CardCnt);
cardStyleInfo.CardLackValues = Enumerable.Repeat((int)cardV, group.LZCards.Count).ToArray();
return true;
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
for (int i = 0; i < groupCnt; i++)
{
var cardGroup = group.CardGroupList[i];
if (cardGroup.Count == CardCnt && IsSameGroupGreater(cardGroup, eatStyleInfo))
{
groupList.Add(new CardGroupList(cardGroup));
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1)
.SelectMany(kv => kv.Value).OrderByDescending(x=>x.Count).ToList();
var groupList = new List<CardGroupList>();
foreach (var cardGroup in cardGroupList)
{
if (cardGroup.Count > 0
&& cardGroup.First().Value > eatStyleInfo.CardValue
&& !IsJoker(cardGroup))
{
var needCnt = CardCnt - cardGroup.Count;
if (needCnt < group.LZCards.Count)
{
List<Card> tmpList = new List<Card>();
tmpList.AddRange(cardGroup);
tmpList.AddRange(group.LZCards.GetRange(0, needCnt));
groupList.Add(new CardGroupList(tmpList));
}
}
}
if (groupList.Count == 0
&& group.LZCards.Count >= CardCnt
&& IsAllSame(group.LZCards)
&& group.LZCards.First().Value > eatStyleInfo.CardValue)
{
var cardList = group.LZCards.GetRange(0, CardCnt);
groupList.Add(new CardGroupList(cardList));
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a5e550c003fb4dc2ad87fd3fd0be483f
timeCreated: 1741684811

View File

@ -0,0 +1,105 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle41Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle41;
public override int CardCnt => 6;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
for (int i = 0; i < group.CardGroupList.Count; i++)
{
if (group.CardGroupList[i].Count == 4)
{
cardStyleInfo = new CardStyleInfo(CardStyle, i, group.Cards.Count);
return true;
}
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹
int lzCount = group.LZCards.Count;
int fourCV = 0;
List<int> lackValues = new List<int>(group.LZCards.Count);
for (int i = group.CardGroupList.Count - 1; i >= 0; i--)
{
int cardV = i;
int count = group.CardGroupList[i].Count;
int needCnt = 0;
if ((4 - count) <= lzCount)
{
// 找到4张相同的牌
needCnt = 4 - count;
fourCV = cardV;
}
lzCount -= needCnt;
lackValues.AddRange(Enumerable.Repeat(cardV, needCnt));
}
if (lzCount > 0)
{
// 可能还会剩下癞子 (剩下得癞子当成普通牌)
// 也可以处理成替代剩下得牌
lzCount = 0;
}
if (fourCV != 0 && lzCount == 0)
{
cardStyleInfo = new CardStyleInfo(CardStyle, fourCV, CardCnt);
cardStyleInfo.CardLackValues = lackValues.ToArray();
return true;
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
if (!group.CardGroupDic.ContainsKey(4)) return groupList;
//所有得四张牌组
foreach (var fourCardGroup in group.CardGroupDic[4])
{
if (!IsSameGroupGreater(fourCardGroup, eatStyleInfo)) continue;
// 找两个单张 (不拆牌)
if (group.CardGroupDic.ContainsKey(1) && group.CardGroupDic[1].Count >= 2)
{
var cardGroup42 = new CardGroupList(fourCardGroup);
cardGroup42.AddRange(group.CardGroupDic[1][0]);
cardGroup42.AddRange(group.CardGroupDic[1][1]);
groupList.Add(cardGroup42);
} else if (group.CardGroupDic.ContainsKey(2) && group.CardGroupDic[2].Count >= 1)
{
var cardGroup42 = new CardGroupList(fourCardGroup);
cardGroup42.AddRange(group.CardGroupDic[2][0]);
groupList.Add(cardGroup42);
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{// 这里不处理直接找炸弹
return new List<CardGroupList>();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e7ace734247b43a3b13487dc54d7359d
timeCreated: 1741684811

View File

@ -0,0 +1,111 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle42Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle42;
public override int CardCnt => 8;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
int mainValue = 0, withValue = 0, withNum = 0;
for (int i = 0; i < group.CardGroupList.Count; i++)
{
if (group.CardGroupList[i].Count == 4)
{
mainValue = i;
}
if (group.CardGroupList[i].Count == 2)
{
withValue = i;
withNum++;
}
}
if (mainValue > 0 && withNum == 2)
{
cardStyleInfo = new CardStyleInfo(CardStyle, mainValue, group.Cards.Count);
return true;
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
if (IsAllSame(group.NoLZCards)) return false; // 直接组成炸弹
int lzCount = group.LZCards.Count;
int fourCV = 0;
int pairCnt = 0;
List<int> lackValues = new List<int>(group.LZCards.Count);
for (int i = group.CardGroupList.Count - 1; i >= 0; i--)
{
int cardV = i;
int count = group.CardGroupList[i].Count;
int needCnt = 0;
if ((4 - count) <= lzCount)
{
// 找到4张相同的牌
needCnt = 4 - count;
fourCV = cardV;
} else if ((2 - count) <= lzCount && (2 - count) >= 0)
{
// 两对
needCnt = 2 - count;
pairCnt++;
}
lzCount -= needCnt;
lackValues.AddRange(Enumerable.Repeat(cardV, needCnt));
}
if (fourCV != 0 && lzCount == 0 && pairCnt == 2)
{
cardStyleInfo = new CardStyleInfo(CardStyle, fourCV, CardCnt);
cardStyleInfo.CardLackValues = lackValues.ToArray();
return true;
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
if (!group.CardGroupDic.ContainsKey(4)) return groupList;
//所有得四张牌组
foreach (var fourCardGroup in group.CardGroupDic[4])
{
if (!IsSameGroupGreater(fourCardGroup, eatStyleInfo)) continue;
// 找两个单张 (不拆牌)
if (group.CardGroupDic.ContainsKey(2) && group.CardGroupDic[2].Count >= 2)
{
var cardGroup42 = new CardGroupList(fourCardGroup);
cardGroup42.AddRange(group.CardGroupDic[2][0]);
cardGroup42.AddRange(group.CardGroupDic[2][1]);
groupList.Add(cardGroup42);
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{// 这里不处理直接找炸弹
return new List<CardGroupList>();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 89f4eda5352c47999ae95ed5cfc018bc
timeCreated: 1741684811

View File

@ -0,0 +1,82 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle510KHelper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle510K;
public override int CardCnt => 3;
protected List<int> V_510K = new List<int>(){DataCardConst.CardValue5, DataCardConst.CardValue10, DataCardConst.CardValueK};
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt)
{
var values = group.Cards.Select(x=>(int)x.Value).ToList();
values.Sort();
if (V_510K.SequenceEqual(values))
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt && group.LZCards.Count == 1)
{ // 只有一个癞子,不然会被当成三个相同
var values = group.Cards.Select(x=>(int)x.Value).ToList();
var exceptV = values.Except(V_510K).ToList();
if (exceptV.Count == 1)
{ // 只有一个不同
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
cardStyleInfo.CardLackValues = new[] { exceptV.First() };
return true;
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupList = new List<CardGroupList>(1);
CardGroupList cardGroupList = new CardGroupList();
for (int i = 0; i < V_510K.Count; i++)
{
if (group.CardGroupList[V_510K[i]].Count > 0)
cardGroupList.Add(group.CardGroupList[V_510K[i]].First());
}
if (cardGroupList.Count == CardCnt)
groupList.Add(cardGroupList);
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupList = new List<CardGroupList>(1);
CardGroupList cardGroupList = new CardGroupList();
for (int i = 0; i < V_510K.Count; i++)
{
if (group.CardGroupList[V_510K[i]].Count > 0)
cardGroupList.Add(group.CardGroupList[V_510K[i]].First());
}
if (cardGroupList.Count == 2)
{
cardGroupList.Add(group.LZCards.First());
groupList.Add(cardGroupList);
}
return new List<CardGroupList>(0);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9c9da57bc34c4c26a04ccdecd95fa88b
timeCreated: 1741784659

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 多副510K (可带 510k单牌)
/// </summary>
public class CardStyle510KMultExtendHelper : CardStyle510KMultHelper
{
public CardStyle510KMultExtendHelper(int mult) : base(mult)
{
}
internal override List<CardGroupList> GetAllStyleCards(CardBaseGroup group, bool isSplitCard)
{
List<CardGroupList> ret = new List<CardGroupList>();
var c510KList = CardFunc.Get510KList(group.SelfCards, _mult);
if (c510KList.Count > 0)
{
// 剩下得 5 10 k也添加上
var c510KGroup = new CardGroupList(c510KList.SelectMany(x => x));
var c510KCards = group.SelfCards.Where(x => V_510K.Contains(x.Value)).ToList();
c510KGroup.AddRange(c510KCards);
ret.Add(c510KGroup);
}
return ret;
}
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt)
{
var c510KList = CardFunc.Get510KList(group.Cards, _mult);
var c510KListLong = c510KList.SelectMany(x => x).ToList();
var remainCardList = group.Cards.Where(x => !c510KListLong.Contains(x) && V_510K.Contains(x.Value)).ToList();
if (remainCardList.Count + c510KListLong.Count == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, 0, c510KList.Count);
cardStyleInfo.CustomInt = c510KList.Count; // 510K的个数
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0ea53d109ec74ae794ff0f676067516c
timeCreated: 1779787226

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 多副510K
/// </summary>
public class CardStyle510KMultHelper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle510KMult;
public override int CardCnt { get; } = 0;
protected int _mult;
protected List<int> V_510K = new List<int>(){DataCardConst.CardValue5, DataCardConst.CardValue10, DataCardConst.CardValueK};
/// <summary>
/// 几幅起算
/// </summary>
public CardStyle510KMultHelper(int mult)
{
CardCnt = mult * 3;
_mult = mult;
}
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt)
{
var c510KList = CardFunc.Get510KList(group.Cards, _mult);
if (c510KList.Count * 3 == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, 0, c510KList.Count);
cardStyleInfo.CustomInt = c510KList.Count; // 510K的个数
return true;
}
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
return GetAllStyleCards(group, false);
}
internal override List<CardGroupList> GetAllStyleCards(CardBaseGroup group, bool isSplitCard)
{
List<CardGroupList> ret = new List<CardGroupList>();
var c510KList = CardFunc.Get510KList(group.SelfCards, _mult);
if (c510KList.Count > 0)
ret.Add(new CardGroupList(c510KList.SelectMany(x=>x)));
return ret;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 871d6a388b274cd9acb178bdf5637f0d
timeCreated: 1779076036

View File

@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 正510K
/// </summary>
public class CardStyle510KPlusHelper : CardStyle510KHelper
{
public override int CardStyle => DataCardConst.CardStyle510KPlus;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
var isRight = base.CheckStyleNormal(group, out cardStyleInfo);
if (isRight)
return group.Cards.Select(x => x.Suit).AllElementsSame();
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
var isRight = base.CheckStyleNormal(group, out cardStyleInfo);
if (isRight)
return group.Cards.Select(x => x.Suit).AllElementsSame();
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{// 只提示一个
var groupList = new List<CardGroupList>(1);
List<Card> cards = new List<Card>();
foreach (var v in V_510K)
{
if (group.CardGroupList[v].Count > 0)
cards.AddRange(group.CardGroupList[v]);
}
// 按照花色分组
var suitCardDict = cards.GroupBy(x => x.Suit)
.ToDictionary(g => g.Key, g => g.Distinct(new CardValueEqualityComparer()).ToList());
foreach (var suitCards in suitCardDict.Values)
{
if (suitCards.Count != CardCnt) continue;
groupList.Add(new CardGroupList(suitCards));
break;
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupList = new List<CardGroupList>(1);
List<Card> cards = new List<Card>();
foreach (var v in V_510K)
{
if (group.CardGroupList[v].Count > 0)
cards.AddRange(group.CardGroupList[v]);
}
// 按照花色分组
var suitCardDict = cards.GroupBy(x => x.Suit)
.ToDictionary(g => g.Key, g => g.Distinct().ToList());
foreach (var suitCards in suitCardDict.Values)
{
if (suitCards.Count != 2) continue;
var cardGroupList = new CardGroupList(suitCards);
cardGroupList.Add(group.LZCards.First());
groupList.Add(cardGroupList);
break;
}
return new List<CardGroupList>(0);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ec36b04d78bd4e08808ff6ecfa8fe62c
timeCreated: 1741828989

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public class CardStyle5Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyle5;
public override int CardCnt => 2;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count == CardCnt
&& group.Cards.Count(x=>DataCardConst.JokerValues.Contains(x.Value)) == CardCnt
&& group.Cards[0].Value != group.Cards[1].Value)
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupList = new List<CardGroupList>(1);
if (group.CardGroupList[18].Count>0 && group.CardGroupList[19].Count>0)
{
var cardGroup = new CardGroupList();
cardGroup.AddRange(group.CardGroupList[18]);
cardGroup.AddRange(group.CardGroupList[19]);
groupList.Add(cardGroup);
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
return new List<CardGroupList>(0);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14e95f3c416848eab6ef7899c7b535f6
timeCreated: 1741684811

View File

@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 全王炸弹
/// </summary>
public class CardStyleAllJokerBombHelper: CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyleAllJokerBomb;
public override int CardCnt => mCardCnt;
private int mCardCnt = 4;
public CardStyleAllJokerBombHelper() { }
/// <summary>
/// 构造
/// </summary>
/// <param name="cardCnt">几个王起算炸</param>
public CardStyleAllJokerBombHelper(int cardCnt)
{
mCardCnt = cardCnt;
}
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt)
{
var isOtherCards = group.Cards.Any(x=>!DataCardConst.JokerValues.Contains(x.Value));
if (isOtherCards) return false;
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
// 癞子不当王
cardStyleInfo = null;
return false;
}
internal override List<CardGroupList> GetAllStyleCards(CardBaseGroup group, bool isSplitCard)
{
List<CardGroupList> ret = new List<CardGroupList>();
var jokerCards = group.SelfCards.Where(x=>DataCardConst.JokerValues.Contains(x.Value)).ToList();
if (jokerCards.Count < CardCnt) return ret;
CardGroupList cardGroupList = new CardGroupList();
cardGroupList.AddRange(jokerCards);
ret.Add(cardGroupList);
return ret;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
return GetAllStyleCards(group, false);
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
// 癞子不当王
return new List<CardGroupList>();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d6cb6966142468396f4d242cd41eab0
timeCreated: 1748936998

View File

@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 连炸 3连以上
/// CardStyleInfo.CustomInt: 表示几个相同炸弹组成
/// </summary>
public class CardStyleBomb123Helper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyleBomb123;
public override int CardCnt => 12;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt)
{
var keysCnt = group.CardGroupDic.Keys.Count;
if (keysCnt != 1) return false;
var bombSize = group.CardGroupDic.Keys.First(); // 单个炸弹的大小
if (bombSize < 4) return false;
// 判断是否连续
if (IsContinuous(group.CardGroupList, bombSize, out var shunCnt)
&& shunCnt.Count * bombSize == group.Cards.Count)
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
cardStyleInfo.CustomInt = group.Cards.Count / bombSize;
return true;
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count < CardCnt) return false;
var lzCnt = group.LZCards.Count;
var bombSize = group.CardGroupDic.Keys.Max(); // 最大的数量作为单个炸弹的大小
if (bombSize < 4) lzCnt -= (bombSize - 4);
if (lzCnt < 0) return false;
for (int i = 0; i < group.CardGroupDic.Keys.Count; i++)
{
var key = group.CardGroupDic.Keys.ElementAt(i);
var vList = group.CardGroupDic[key];
var diffSize = bombSize - key;
lzCnt -= diffSize * vList.Count;
if (lzCnt < 0) return false;
}
// 判断是否连续
if (IsMaxContinuousByLZ(group.CardGroupList, bombSize, group.LZCards.Count,
out var shunValueList, out var lackValueList)
&& shunValueList.Count * bombSize == group.Cards.Count)
{
cardStyleInfo = new CardStyleInfo(CardStyle, shunValueList[0], shunValueList.Count * bombSize);
cardStyleInfo.CardLackValues = lackValueList.ToArray();
cardStyleInfo.CustomInt = group.Cards.Count / bombSize;
return true;
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
// 找最大的连炸
List<Card> continuousList = new List<Card>();
int minBombSize = Int32.MaxValue;
for (int i = 0; i < group.CardGroupList.Count; i++)
{
if (group.CardGroupList[i].Count >= 4)
{
minBombSize = Math.Min(minBombSize, group.CardGroupList[i].Count);
continuousList.AddRange(group.CardGroupList[i]);
} else if (continuousList.Count >= CardCnt)
{
// 把一些多余的牌删除
var filterList = continuousList.GroupBy(c=>c.Value)
.Select(g=>
Tuple.Create(g.Key,g.ToList().GetRange(0, minBombSize)))
.SelectMany(g=>g.Item2)
.ToList();
// 判断是否属于连炸
var targetGroup = group.mDeck.CreateCardGroup(filterList);
if (targetGroup.GetCardStyleInfo().CardStyle == CardStyle)
{
groupList.Add(new CardGroupList(filterList));
}
minBombSize = Int32.MaxValue;
continuousList.Clear();
}
else if (continuousList.Count > 0)
{
minBombSize = Int32.MaxValue;
continuousList.Clear();
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupList = new List<CardGroupList>();
// 找最大的连炸
List<Card> continuousList = new List<Card>();
int lzCnt = group.LZCards.Count;
int continuousCnt = 0;
// 2233344444555
for (int i = 0; i < group.CardGroupList.Count; i++)
{
continuousList.Clear();
continuousCnt = 0;
for (int j = 0; j < group.CardGroupList.Count; j++)
{
var index = i + j;
if (index >= group.CardGroupList.Count) break;
if (group.CardGroupList[index].Count == 0) break;
if (group.CardGroupList[index].Count >= 4
|| group.CardGroupList[index].Count + lzCnt >= 4)
{
continuousList.AddRange(group.CardGroupList[index]);
continuousCnt++;
if (continuousCnt >= 3)
{
groupList.Add(new CardGroupList(continuousList));
}
} else
{
continuousList.Clear();
continuousCnt = 0;
}
}
}
// 继续检查每个连炸是否符合要求
for (int i = groupList.Count - 1; i >= 0 ; i--)
{
var bomb = groupList[i];
var bombDict = bomb.GroupBy(c => c.Value)
.ToDictionary(g => g.Key, g => g.ToList());
var bombLine = bombDict.Values.Select(g => g.Count).ToList();
var bombMax = bombLine.Max();
int needCnt = 0;
if (bombMax >= 4)
{
int oldBomMax = bombMax;
while (bombMax >= 4)
{
needCnt = 0;
for (int j = 0; j < bombLine.Count; j++)
{
needCnt += bombMax - bombLine[j];
}
if (needCnt <= lzCnt) break;
bombMax--;
}
if (needCnt > lzCnt)
{
groupList.RemoveAt(i);
continue;
}
// 删除多余得牌
foreach (var bd in bombDict)
{
var rmCards = bd.Value.GetRange(0, oldBomMax - bombMax);
for (int j = rmCards.Count - 1; j >= 0 ; j--)
{
bomb.Remove(rmCards[j]);
}
}
}
else
{
for (int j = 0; j < bombLine.Count; j++)
{
needCnt += 4 - bombLine[j];
}
if (needCnt > lzCnt)
{
groupList.RemoveAt(i);
continue;
}
}
// 再检验一次
bomb.AddRange(group.LZCards.GetRange(0, needCnt));
var bombGroup = group.mDeck.CreateCardGroup(bomb);
if (bombGroup.GetCardStyleInfo().CardStyle != CardStyle)
{
groupList.RemoveAt(i);
}
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3b2b8f2ee2ed48f2991a8c65d46732fa
timeCreated: 1749020659

View File

@ -0,0 +1,12 @@
namespace GameFix.Poker
{
/// <summary>
/// 连炸 2连以上
/// CardStyleInfo.CustomInt: 表示几个相同炸弹组成
/// </summary>
public class CardStyleBomb12Helper : CardStyleBomb123Helper
{
public override int CardStyle => DataCardConst.CardStyleBomb12;
public override int CardCnt => 8;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b58721a51e0b4276a356d4e7b914c66d
timeCreated: 1765874379

View File

@ -0,0 +1,120 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 炸弹 (四张以上)
/// </summary>
public class CardStyleBombHelper : CardStyleBaseHelper
{
public override int CardStyle => DataCardConst.CardStyleBomb;
public override int CardCnt => 4;
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt && IsAllSame(group.Cards))
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.Cards);
return true;
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count < CardCnt) return false;
if (group.LZCards.Count == group.Cards.Count && IsAllSame(group.LZCards))
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards);
return true;
}
if (IsAllSame(group.NoLZCards))
{
var cardV = group.NoLZCards[0].Value;
cardStyleInfo = new CardStyleInfo(CardStyle, cardV, group.Cards.Count);
cardStyleInfo.CardLackValues = Enumerable.Repeat((int)cardV, group.LZCards.Count).ToArray();
return true;
}
return false;
}
internal override List<CardGroupList> GetAllStyleCards(CardBaseGroup group, bool isSplitCard)
{
List<CardGroupList> ret = new List<CardGroupList>();
var groupCnt = group.CardGroupList.Count;
for (int i = 0; i < groupCnt; i++)
{
var cardGroup = group.CardGroupList[i];
if (cardGroup.Count >= CardCnt)
{
ret.Add(new CardGroupList(cardGroup));
}
}
return ret;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
for (int i = 0; i < groupCnt; i++)
{
var cardGroup = group.CardGroupList[i];
if (cardGroup.Count >= CardCnt)
{
var cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, cardGroup);
if (eatStyleInfo == null || group.mStyleComparer.Greater(cardStyleInfo, eatStyleInfo))
{
groupList.Add(new CardGroupList(cardGroup));
}
}
}
return groupList;
}
internal override List<CardGroupList> GetAllEatTipGroupWithLZ(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var cardGroupList = group.CardGroupDic.Where(kv => kv.Key >= 1)
.SelectMany(kv => kv.Value).OrderByDescending(x=>x.Count).ToList();
var groupList = new List<CardGroupList>();
if (eatStyleInfo == null)
eatStyleInfo = new CardStyleInfo(CardStyle, 0, CardCnt);
foreach (var cardGroup in cardGroupList)
{
if (cardGroup.Count == 0 || IsJoker(cardGroup)) continue;
var totalCnt = cardGroup.Count + group.LZCards.Count;
if (totalCnt < CardCnt) continue;
var tmpStyleInfo = new CardStyleInfo(CardStyle, cardGroup.First().Value, totalCnt);
int needCnt = 0;
if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo))
{
needCnt = tmpStyleInfo.CardCnt - cardGroup.Count;
List<Card> tmpList = new List<Card>();
tmpList.AddRange(cardGroup);
tmpList.AddRange(group.LZCards.GetRange(0, needCnt));
groupList.Add(new CardGroupList(tmpList));
}
}
if (groupList.Count == 0)
{ // 癞子是否可以组成炸弹
var tmpStyleInfo = new CardStyleInfo(CardStyle, group.LZCards.First().Value, group.LZCards.Count);
if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo))
groupList.Add(new CardGroupList(group.LZCards));
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ee5d4c36a107495aa91cf175575de0d5
timeCreated: 1741848881

View File

@ -0,0 +1,168 @@
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
/// <summary>
/// 带王炸弹 (四张以上)
/// </summary>
public class CardStyleJokerBombHelper : CardStyleBombHelper
{
public override int CardStyle => DataCardConst.CardStyleJokerBomb;
public override int CardCnt => 4;
private bool isAllJoker = true; // 是否可以组成全王炸弹
public CardStyleJokerBombHelper()
{
}
public CardStyleJokerBombHelper(bool allJoker)
{
isAllJoker = allJoker;
}
internal override bool CheckStyleNormal(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count >= CardCnt)
{
// 先排除王
var bombCards = group.Cards.Where(x=>!DataCardConst.JokerValues.Contains(x.Value)).ToList();
// 判断是否都是王
if (isAllJoker && bombCards.Count == 0)
{
cardStyleInfo = new CardStyleInfo(CardStyle, DataCardConst.JokerValues[0], group.Cards.Count);
return true;
}
if (IsAllSame(bombCards) && bombCards.Count >= CardCnt)
{
cardStyleInfo = new CardStyleInfo(CardStyle, bombCards.First().Value, group.Cards.Count);
return true;
}
}
return false;
}
internal override bool CheckStyleWithLZ(CardBaseGroup group, out CardStyleInfo cardStyleInfo)
{
cardStyleInfo = null;
if (group.Cards.Count < CardCnt) return false;
if (group.LZCards.Count == group.Cards.Count && IsAllSame(group.LZCards))
{
cardStyleInfo = CardStyleHelper.CreateCardStyleInfo(CardStyle, group.LZCards);
return true;
}
// 先排除王
var bombCards = group.NoLZCards.Where(x=>!DataCardConst.JokerValues.Contains(x.Value)).ToList();
// 判断是否都是王
if (isAllJoker && bombCards.Count == 0)
{
cardStyleInfo = new CardStyleInfo(CardStyle, DataCardConst.JokerValues[0], group.Cards.Count);
return true;
}
if (IsAllSame(bombCards) && bombCards.Count + group.LZCards.Count >= CardCnt)
{
var cardV = bombCards[0].Value;
cardStyleInfo = new CardStyleInfo(CardStyle, cardV, group.Cards.Count);
cardStyleInfo.CardLackValues = Enumerable.Repeat((int)cardV, group.LZCards.Count).ToArray();
return true;
}
return false;
}
internal override List<CardGroupList> GetAllEatTipGroup(CardBaseGroup group, CardStyleInfo eatStyleInfo)
{
var groupCnt = group.CardGroupList.Count;
var groupList = new List<CardGroupList>(groupCnt);
var jokerList = group.Cards.Where(x=>DataCardConst.JokerValues.Contains(x.Value)).ToList();
if (eatStyleInfo == null)
eatStyleInfo = new CardStyleInfo(CardStyle, 0, CardCnt);
if (isAllJoker && jokerList.Count >= CardCnt)
{ // 先提示全王
var tmpStyleInfo = new CardStyleInfo(CardStyle, DataCardConst.JokerValues[0], jokerList.Count);
if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo))
{
groupList.Add(new CardGroupList(jokerList));
}
}
var filterGroupList = group.CardGroupList.Where(x => x.Count >= CardCnt).ToList();
for (int i = 0; i < filterGroupList.Count; i++)
{
var cardGroup = filterGroupList[i];
if (IsJoker(cardGroup)) continue;
var tmpStyleInfo = new CardStyleInfo(CardStyle, cardGroup.First().Value, cardGroup.Count);
if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo) && filterGroupList.Count > 1)
{
groupList.Add(new CardGroupList(cardGroup));
} else {
tmpStyleInfo = new CardStyleInfo(CardStyle, cardGroup.First().Value, cardGroup.Count + jokerList.Count);
if (group.mStyleComparer.Greater(tmpStyleInfo, eatStyleInfo))
{
var cardGroupList = new CardGroupList(cardGroup);
cardGroupList.AddRange(jokerList);
groupList.Add(cardGroupList);
}
}
}
return groupList;
}
/// <summary>
/// 获取所有得炸弹
/// </summary>
public List<CardGroupList> GetAllBombs(CardBaseGroup group)
{
List<CardGroupList> groupList = new List<CardGroupList>();
var jokerList = group.Cards
.Where(x => DataCardConst.JokerValues.Contains(x.Value) && x.CardType != DataCardConst.CardTypeLZ)
.ToList();
var descCardGroupList = group.CardGroupList.Select(x => x).OrderByDescending(x => x.Count).ToList();
var lzCnt = group.LZCards.Count;
for (int i = 0; i < descCardGroupList.Count; i++)
{
if (IsJoker(descCardGroupList[i]) ||descCardGroupList[i].Count + lzCnt < CardCnt) continue;
CardGroupList cardGroupList = new CardGroupList();
cardGroupList.AddRange(descCardGroupList[i]);
if (lzCnt > 0)
{
cardGroupList.AddRange(group.LZCards);
lzCnt = 0;
}
if (jokerList.Count > 0)
{
cardGroupList.AddRange(jokerList);
jokerList.Clear();
}
CardStyleInfo styleInfo =
new CardStyleInfo(CardStyle, descCardGroupList[i].First().Value, cardGroupList.Count);
cardGroupList.CardStyleInfo = styleInfo;
groupList.Add(cardGroupList);
}
if (jokerList.Count > CardCnt && isAllJoker)
{
CardGroupList cardGroupList = new CardGroupList();
cardGroupList.AddRange(jokerList);
jokerList.Clear();
CardStyleInfo styleInfo =
new CardStyleInfo(CardStyle, jokerList.First().Value, cardGroupList.Count);
cardGroupList.CardStyleInfo = styleInfo;
groupList.Add(cardGroupList);
}
return groupList;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b4ef4d49fafc4db09cc09e3d471c45c5
timeCreated: 1741850722

View File

@ -0,0 +1,205 @@
using System;
using GameData;
using GameMessage;
using UnityGame;
namespace GameFix
{
/// <summary>
/// int[] Seat
/// - 下标索引表示进入桌子的初始位置号,也是服务器原始玩家数组的索引
/// - 索引对应的内容是存在打乱之后的位置信息
/// </summary>
public static class GameDeskUtility
{
#region GameDeskInfo
/// <summary>
/// 初始化位置信息
/// </summary>
public static void InitDeskSeat(this GameDeskInfo deskInfo, int playerCnt)
{
deskInfo.Seat = new int[playerCnt];
for (int i = 0; i < playerCnt; i++)
{
deskInfo.Seat[i] = i;
}
}
public static void UpdateSeat(this GameDeskInfo deskInfo, int[] seat)
{
deskInfo.Seat = (int[])seat.Clone();
}
public static int GetServerSeat(this GameDeskInfo deskInfo, int serverSeat)
{
return GetServerSeat(deskInfo.Seat, serverSeat);
}
public static int GetNextServerSeat(this GameDeskInfo deskInfo, int serverRealSeat)
{
return GetNextServerSeat(deskInfo.Seat, serverRealSeat);
}
public static int GetNextServerSeat(this GameDeskInfo deskInfo, int serverRealSeat, int n)
{
return GetNextServerSeat(deskInfo.Seat, serverRealSeat, n);
}
public static void SwitchSeat(this GameDeskInfo deskInfo, int originalSeat, int targetSeat)
{
SwitchSeat(deskInfo.Seat, originalSeat, targetSeat);
}
#if !Server
public static int ClientToServerSeat(this GameDeskInfo deskInfo, int clientSeat)
{
return ClientToServerSeat(deskInfo.Seat, clientSeat);
}
public static int ServerToClientSeat(this GameDeskInfo deskInfo, int serverSeat)
{
return ServerToClientSeat(deskInfo.Seat, serverSeat);
}
#endif
#endregion
#if !Server
/// <summary>
/// 客户端转服务器位置
/// </summary>
public static int ClientToServerSeat(int[] seat, int clientSeat)
{
if (UnityGame.hjhaGlobale.Instance.userData == null)
{
return -1;
}
if (seat == null || seat.Length < clientSeat)
{
return -1;
}
if (seat.Length <= 0)
{
return -1;
}
if (clientSeat < 0)
{
return 0;
}
int mySeat = UnityGame.hjhaGlobale.Instance.userData.seat;
mySeat = Array.IndexOf(seat, mySeat);
if (mySeat < 0)
{
if (hjhaGlobale.Instance.userData.state == (int)PlayerState.Desk
|| hjhaGlobale.Instance.userData.state == (int)PlayerState.Ready
|| hjhaGlobale.Instance.userData.state == (int)PlayerState.War)
{
UnityEngine.Debug.LogError($"位置信息错误MySeat={mySeat}");
}
return -1;
}
int serverSeat = (mySeat + clientSeat) % seat.Length;
return GetServerSeat(seat, serverSeat);
}
/// <summary>
/// 服务器转客户端位置
/// </summary>
public static int ServerToClientSeat(int[] seat, int serverSeat)
{
if (UnityGame.hjhaGlobale.Instance.userData == null)
{
return -1;
}
if (seat == null || seat.Length < serverSeat)
{
return -1;
}
int seatCnt = seat.Length;
if (seatCnt <= 0)
{
return -1;
}
if (serverSeat < 0)
{
return -1;
}
int mySeat = UnityGame.hjhaGlobale.Instance.userData.seat;
mySeat = Array.IndexOf(seat, mySeat);
serverSeat = Array.IndexOf(seat, serverSeat);
if (mySeat < 0 || serverSeat < 0)
{
UnityEngine.Debug.LogError($"位置信息错误ServerSeat={serverSeat} MySeat={mySeat}");
return -1;
}
return (serverSeat - mySeat + seatCnt) % seatCnt;
}
#endif
/// <summary>
/// 获取真实的服务器位置
/// </summary>
public static int GetServerSeat(int[] seat, int serverSeat)
{
if (seat == null || seat.Length < serverSeat)
{
#if Server
MrWu.Debug.Debug.Error($"位置信息错误serverSeat={serverSeat}");
#endif
return serverSeat;
}
return seat[serverSeat];
}
/// <summary>
/// 获取 [serverSeat] 下N个玩家的服务器的位置
/// </summary>
public static int GetNextServerSeat(int[] seat, int serverSeat, int n)
{
if (seat == null || seat.Length < serverSeat)
{
#if Server
MrWu.Debug.Debug.Error($"位置信息错误serverSeat={serverSeat}");
#endif
return serverSeat;
}
serverSeat = Array.IndexOf(seat, serverSeat);
if (serverSeat < 0)
{
#if Server
MrWu.Debug.Debug.Error($"位置信息错误serverSeat={serverSeat}");
#endif
return serverSeat;
}
var nextSeat = (serverSeat + n) % seat.Length;
return GetServerSeat(seat, nextSeat);
}
/// <summary>
/// 获取 [serverSeat] 下一个玩家的服务器的位置
/// </summary>
public static int GetNextServerSeat(int[] seat, int serverSeat)
{
return GetNextServerSeat(seat, serverSeat, 1);
}
/// <summary>
/// 交换位置
/// </summary>
public static void SwitchSeat(int[] seat, int originalSeat, int targetSeat)
{
(seat[originalSeat], seat[targetSeat]) = (seat[targetSeat], seat[originalSeat]);
}
}
}

View File

@ -0,0 +1,40 @@
using System.Collections.Generic;
using GameMessage;
namespace GameFix.Poker
{
public abstract class GameOverInfo1<T> where T : ICardInfoType1
{
public string NickName { get; set; }
public int Sex { get; set; }
public string avter { get; set; }
public int UserId { get; set; }
/// <summary>
/// 赏的数值
/// </summary>
public int Shang { get; set; }
/// <summary>
/// 讨赏分
/// </summary>
public int ShangScore { get; set; }
/// <summary>
/// 输赢分
/// </summary>
public int ShuYin { get; set; }
/// <summary>
/// 得分
/// </summary>
public int DeFen { get; set; }
/// <summary>
/// 总得分
/// </summary>
public int ZongDeFen { get; set; }
public List<T> ZhaDans { get; set; }
}
}

View File

@ -0,0 +1,21 @@
#if GameClient
using System.Collections.Generic;
using UnityEngine;
using GameMessage;
namespace GameFix.Poker
{
public abstract class LiCardType1<T> where T : ICardInfoType1
{
/// <summary>
/// 整理的牌内容
/// </summary>
public List<T> Cards;
/// <summary>
/// 这些牌的背景颜色
/// </summary>
public Color Color;
}
}
#endif

View File

@ -0,0 +1,722 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GameMessage;
namespace GameFix.Poker
{
public static class PokerLogic
{
public class XY
{
public int x; //牌面大小
public int y; //次数
}
/// <summary>
/// 获取所有的单张
/// </summary>
/// <returns></returns>
public static List<T> GetDanZhang<T>(IPlayOutCardType1 outCard, T[] cards, bool chaiDan = false)
where T : ICardInfoType1
{
if (outCard.GameNum <= 0 || cards == null || cards.Length <= 0) return null;
Dictionary<byte, byte> dic = new Dictionary<byte, byte>();
List<T> result = new List<T>();
foreach (var item in cards)
{
if (item.GameNum <= outCard.GameNum) continue;
if (dic.ContainsKey(item.GameNum))
{
if (dic[item.GameNum] == 1)
{
dic[item.GameNum]++;
if (!chaiDan || (chaiDan && dic[item.GameNum] >= 4))
result.RemoveAll(x => x.GameNum == item.GameNum);
}
}
else
{
dic[item.GameNum] = 1;
result.Add(item);
}
}
return result;
}
/// <summary>
/// 获取连续的相同大小的张数
/// </summary>
/// <param name="outCard"></param>
/// <param name="cards">手里没有打下去的牌</param>
/// <param name="count">几张大小相同的个数</param>
/// <param name="shouPai">手里全部的牌</param>
/// <returns></returns>
public static List<List<T>> GetLianCount<T>(IPlayOutCardType1 outCard, T[] cards, int count,
T[] shouPai) where T : ICardInfoType1
{
if (outCard.GameNum <= 0 || cards == null || cards.Length <= 3) return null;
int lian = 0; //连了几下
if (outCard.Type == CardType1.LianDui)
{
lian = outCard.Ids.Length / 2; //是几连对
}
if (outCard.Type == CardType1.FeiJi)
{
lian = GetFeiJiCount(GetCardByIds(shouPai, outCard.Ids)); //是几连飞机
}
List<List<T>> result = new List<List<T>>();
var countCard = GetCountCard(outCard, cards, count, true, true, lian - 2);
if (countCard.Count < lian) return result;
for (int i = 0; i < countCard.Count - 1; i++)
{
List<T> temp = new List<T>();
int c = 1;
int num = countCard[i][0].GameNum;
temp.AddRange(countCard[i]);
for (int j = i + 1; j < countCard.Count; j++)
{
if (countCard[j][0].GameNum + 1 == num)
{
temp.AddRange(countCard[j]);
c++;
num = countCard[j][0].GameNum;
}
if (c == lian) break;
}
if (c == lian)
{
result.Add(temp);
}
}
return result;
}
/// <summary>
/// 根据牌的绝对Id返回牌的详细信息
/// </summary>
/// <param name="wanshouPai">玩家手牌</param>
/// <param name="ids">牌的Id集合</param>
/// <returns></returns>
public static T[] GetCardByIds<T>(T[] wanshouPai, byte[] ids) where T : ICardInfoType1
{
if (ids == null || ids.Length <= 0) return null;
if (wanshouPai == null || wanshouPai.Length <= 0) return null;
List<T> result = new List<T>();
foreach (var id in ids)
{
foreach (var item in wanshouPai)
{
if (item.ID == id)
{
result.Add(item);
break;
}
}
}
if (ids.Length != result.Count)
{
throw new Exception($"获取玩家的牌张数不对 idslen:{ids.Length} result len:{result.Count} ");
}
return result.ToArray();
}
/// <summary>
/// 获取飞机的连的个数,长度
/// </summary>
/// <param name="selectCards"></param>
/// <returns></returns>
public static int GetFeiJiCount<T>(T[] selectCards) where T : ICardInfoType1
{
if (selectCards == null || selectCards.Length < 6) return 0;
List<XY> list = new List<XY>();
foreach (var item in selectCards)
{
var t = list.Find(x => x.x == item.GameNum);
if (t != null)
{
t.y++;
}
else
{
list.Add(new XY { x = item.GameNum, y = 1 });
}
}
int num = list.Count(x => x.y >= 3);
if (num < 2) return 0;
list = list.Where(x => x.y >= 3).ToList();
List<int> fj = new List<int>();
for (int i = 0; i < list.Count - 1; i++)
{
if (list[i].x - 1 == list[i + 1].x)
{
if (!fj.Contains(list[i].x))
{
fj.Add(list[i].x);
}
if (!fj.Contains(list[i + 1].x))
{
fj.Add(list[i + 1].x);
}
}
}
return fj.Count;
}
/// <summary>
/// 获取玩家手里几张相同的牌型
/// </summary>
/// <param name="outCard">玩家出的牌</param>
/// <param name="cards">玩家手里没有出下去的牌</param>
/// <param name="count">要获取相同大小的张数</param>
/// <param name="chai">是否拆牌 false不拆 true拆</param>
/// <param name="deng">是否获取跟出牌大小相等的牌 true是 false不是</param>
/// <returns></returns>
public static List<List<T>> GetCountCard<T>(IPlayOutCardType1 outCard, T[] cards, int count,
bool chai = false, bool deng = false, int cha = 0) where T : ICardInfoType1
{
if (count <= 0 || outCard.GameNum <= 0 || cards == null || cards.Length < count) return null;
List<List<T>> result = new List<List<T>>();
Dictionary<byte, byte> dic = new Dictionary<byte, byte>();
foreach (var item in cards)
{
if (deng)
{
if (item.GameNum < outCard.GameNum - cha) continue;
}
else
{
if (item.GameNum <= outCard.GameNum - cha) continue;
}
if (dic.ContainsKey(item.GameNum))
{
dic[item.GameNum]++;
}
else
{
dic[item.GameNum] = 1;
}
}
foreach (var item in dic)
{
if (item.Value >= 4 && count < 4 && chai) continue; //炸弹就不拆
if (!chai)
{
if (item.Value != count) continue;
result.Add(cards.Where(x => x.GameNum == item.Key).ToList());
}
else
{
if (item.Value < count) continue;
result.Add(cards.Where(x => x.GameNum == item.Key).Take(count).ToList());
}
}
return result;
}
/// <summary>
/// 是否是对子
/// 跑得快朋友房使用了
/// </summary>
/// <param name="selectCards"></param>
/// <returns></returns>
public static bool IsDuiZi<T>(T[] selectCards) where T : ICardInfoType1
{
if (selectCards == null || selectCards.Length != 2) return false;
return selectCards[0].GameNum == selectCards[1].GameNum;
}
/// <summary>
/// 获取顺子
/// </summary>
/// <param name="playCard"></param>
/// <returns></returns>
public static List<List<T>> GetShunZi<T>(T[] playCard) where T : ICardInfoType1
{
if (playCard == null || playCard.Length < 5) return null;
var cards = playCard.ToList();
List<List<T>> result = new List<List<T>>();
for (int i = 14; i > 6; i--)
{
List<T> shunzi = new List<T>();
for (int j = 0; j < playCard.Length; j++)
{
T temp = cards.FirstOrDefault(x => x.GameNum == (i - j));
if (temp.ID > 0)
{
shunzi.Add(temp);
}
else
{
break;
}
}
if (shunzi.Count >= 5)
{
result.Add(shunzi);
}
}
return result;
}
/// <summary>
/// 获取顺子
/// </summary>
/// <param name="outCard"></param>
/// <param name="playCard"></param>
/// <returns></returns>
public static List<List<T>> GetShunZi<T>(IPlayOutCardType1 outCard, T[] playCard) where T : ICardInfoType1
{
if (outCard.GameNum <= 0 || outCard.Type != CardType1.ShunZi) return null;
if (playCard == null || playCard.Length < outCard.Ids.Length) return null;
var cards = playCard.ToList();
List<List<T>> result = new List<List<T>>();
for (int i = 14; i > outCard.GameNum; i--)
{
List<T> shunzi = new List<T>();
for (int j = 0; j < outCard.Ids.Length; j++)
{
T temp = cards.FirstOrDefault(x => x.GameNum == (i - j));
if (temp.ID > 0)
{
shunzi.Add(temp);
}
else
{
break;
}
}
if (shunzi.Count == outCard.Ids.Length)
{
result.Add(shunzi);
}
}
return result;
}
/// <summary>
/// 是否是飞机 跑得快朋友房使用了
/// </summary>
/// <param name="selectCards">选中的牌</param>
/// <param name="isPaiBuGou">true不够 false 够</param>
/// <param name="canDaiPia">true可以带牌 false不可以带牌</param>
/// <param name="gameNum">返回飞机面值</param>
/// <returns></returns>
public static bool IsFeiJi<T>(T[] selectCards, bool isPaiBuGou, bool canDaiPia, out byte gameNum)
where T : ICardInfoType1
{
gameNum = 0;
if (selectCards == null || selectCards.Length < 6) return false;
List<XY> list = new List<XY>();
foreach (var item in selectCards)
{
var t = list.Find(x => x.x == item.GameNum);
if (t != null)
{
t.y++;
if (t.y >= 3)
{
if (gameNum == 0)
gameNum = item.GameNum;
}
}
else
{
list.Add(new XY { x = item.GameNum, y = 1 });
}
}
int num = list.Count(x => x.y >= 3);
if (num < 2)
return false; //if (canDaiPia && isPaiBuGou && selectCards.Length < num * 3) return false; //功能相同
list = list.Where(x => x.y >= 3).ToList();
List<int> fj = new List<int>();
for (int i = 0; i < list.Count - 1; i++)
{
if (list[i].x - 1 == list[i + 1].x)
{
if (!fj.Contains(list[i].x))
{
fj.Add(list[i].x);
}
if (!fj.Contains(list[i + 1].x))
{
fj.Add(list[i + 1].x);
}
}
}
num = fj.Count;
if (num < 2) return false;
gameNum = (byte)fj[0];
if (!canDaiPia && num * 3 != selectCards.Length) return false; //如果不能带牌
if (selectCards.Length > num * 3 + num * 2) return false; //带多了
if (canDaiPia && !isPaiBuGou && num * 3 + num * 2 != selectCards.Length) return false;
num--;
for (int i = 0; i < fj.Count; i++)
{
if (num == 1)
{
//2连的飞机
return fj[i] - num == fj[i + num];
}
else if (num == 2)
{
//3连的飞机
return fj[i] == fj[i + 1] + 1 && fj[i + 1] == fj[i + 2] + 1;
}
else if (num == 3)
{
//4连的飞机
return fj[i] == fj[i + 1] + 1 && fj[i + 1] == fj[i + 2] + 1 && fj[i + 2] == fj[i + 3] + 1;
}
else
{
if (fj[i] - num != fj[i + num])
{
return false;
}
}
}
return true;
}
/// <summary>
/// 判断是否是顺子,使用该函数前请先排序 跑得快朋友房使用了
/// </summary>
/// <param name="selectCards"></param>
/// <returns></returns>
public static bool IsShunzi<T>(T[] selectCards) where T : ICardInfoType1
{
if (selectCards == null || selectCards.Length < 5) return false;
T temp = selectCards[0];
for (int i = 1; i < selectCards.Length; i++)
{
if (selectCards[i].GameNum != temp.GameNum - 1) return false; //不是连续的
temp = selectCards[i];
}
return true;
}
/// <summary>
/// 给牌排序 跑得快朋友房使用了
/// </summary>
/// <param name="cards"></param>
public static void SortCardByGameNum<T>(ref T[] cards) where T : ICardInfoType1
{
if (cards == null || cards.Length < 2) return;
Array.Sort(cards, (x, y) => y.GameNum.CompareTo(x.GameNum));
}
/// <summary>
/// 是否是连对,,使用该函数前请先排序 跑得快朋友房使用了
/// </summary>
/// <param name="selectCards"></param>
/// <returns></returns>
public static bool IsLianDui<T>(T[] selectCards) where T : ICardInfoType1
{
if (selectCards == null || selectCards.Length < 4 || selectCards.Length % 2 != 0) return false;
T temp = selectCards[0];
for (int i = 0; i < selectCards.Length; i += 2)
{
if (selectCards[i].GameNum != selectCards[i + 1].GameNum) return false; //不是一对的牌
if (i + 2 <= selectCards.Length - 1)
{
if (temp.GameNum != selectCards[i + 2].GameNum + 1) return false; //不是顺子
temp = selectCards[i + 2];
}
}
return true;
}
/// <summary>
/// 获取玩家炸弹没有炸弹则out就是废王的牌
/// 需要先排序才可以运算
/// </summary>
/// <param name="playCards">玩家手牌</param>
/// <param name="cards">炸弹的牌 或者 废王的牌</param>
/// <returns>true有炸弹 false没有炸弹</returns>
public static bool GetPlayZhaDan<T>(T[] playCards, out List<T> cards) where T : ICardInfoType1
{
cards = new List<T>();
if (playCards == null || playCards.Length <= 0) return false;
List<T> result = new List<T>();
List<T> temp = new List<T>();
List<T> wang = new List<T>();
byte count = 0;
byte num = 0;
foreach (var item in playCards)
{
if (item.GameNum == 98 || item.GameNum == 100)
{
wang.Add(item);
continue;
}
if (num == 0 && count == 0)
{
num = item.GameNum;
count++;
temp.Add(item);
}
else
{
if (num == item.GameNum)
{
count++;
temp.Add(item);
}
else
{
num = item.GameNum;
count = 1;
if (temp.Count >= 4)
{
result.AddRange(temp);
}
temp.Clear();
temp.Add(item);
}
}
}
if (temp.Count >= 4) //防止最后一张牌遗漏
{
result.AddRange(temp);
}
temp.Clear();
if (result.Count >= 4)
{
result.AddRange(wang);
cards.AddRange(result);
return true;
}
cards = wang;
if (wang.Count >= 4)
{
return true;
}
return false;
}
/// <summary>
/// 获取玩家的炸弹
/// </summary>
/// <param name="playCards">玩家手牌</param>
/// <param name="cards">炸弹的牌</param>
/// <returns>true有炸弹 false没有炸弹</returns>
public static bool GetPlayZhaDans<T>(T[] playCards, out List<List<T>> cards) where T : ICardInfoType1
{
cards = new List<List<T>>();
if (playCards == null || playCards.Length <= 0) return false;
List<T> temp = new List<T>();
byte count = 0;
byte num = 0;
foreach (var item in playCards)
{
if (num == 0 && count == 0)
{
num = item.GameNum;
count++;
temp.Add(item);
}
else
{
if (num == item.GameNum)
{
count++;
temp.Add(item);
}
else
{
num = item.GameNum;
count = 1;
if (temp.Count >= 4)
{
cards.Add(temp.ToList());
}
temp.Clear();
temp.Add(item);
}
}
}
if (temp.Count >= 4) //防止最后一张牌遗漏
{
cards.Add(temp.ToList());
}
return cards.Count > 0;
}
/// <summary>
/// 是否是三带二 跑得快朋友房使用了
/// </summary>
/// <param name="selectCards">要判断的牌</param>
/// <param name="isPaiBuGou">玩家手牌是不是不够张数带牌true不够 false够</param>
/// <returns></returns>
#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有)
public static bool IsSanDaiEr<T>(T[] selectCards, bool isPaiBuGou, out byte gameNum) where T : ICardInfoType1
#pragma warning restore CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有)
{
gameNum = 0;
if (selectCards == null || selectCards.Length < 4 || selectCards.Length > 5) return false;
if (!isPaiBuGou && selectCards.Length != 5) return false;
Dictionary<byte, byte> dic = new Dictionary<byte, byte>();
foreach (var item in selectCards)
{
if (dic.ContainsKey(item.GameNum))
{
dic[item.GameNum]++;
}
else
{
dic[item.GameNum] = 1;
}
}
if (dic.Count > 3 || dic.Count < 2) return false;
var b = false;
foreach (var item in dic)
{
if (item.Value >= 3 && item.Key < 90) //大王不能当成3个一起出
{
gameNum = item.Key;
b = true;
break;
}
}
return b;
}
/// <summary>
/// 检测是否有相同的ID因为牌的ID是唯一的不会相同
/// </summary>
/// <param name="ids"></param>
/// <returns>true有 false没有</returns>
public static bool CheckSameId(byte[] ids)
{
if (ids == null || ids.Length <= 1) return false;
Dictionary<byte, int> did = new Dictionary<byte, int>();
for (int i = 0; i < ids.Length; i++)
{
if (did.ContainsKey(ids[i]))
{
did[ids[i]]++;
}
else
{
did[ids[i]] = 1;
}
}
foreach (var id in did)
{
if (id.Value > 1)
{
return true;
}
}
return false;
}
/// <summary>
/// 给牌排序,按照个数从大到小
/// </summary>
/// <param name="cards"></param>
/// <returns></returns>
public static T[] SortCardByCount<T>(T[] cards) where T : ICardInfoType1
{
if (cards == null || cards.Length <= 0) return null;
List<T> result = new List<T>();
Dictionary<int, List<T>> dics = new Dictionary<int, List<T>>();
for (int i = 0; i < cards.Length; i++)
{
if (cards[i].GameNum > 20)
{
result.Add(cards[i]);
continue;
}
if (dics.ContainsKey(cards[i].GameNum))
{
dics[cards[i].GameNum].Add(cards[i]);
}
else
{
var temp = new List<T>();
temp.Add(cards[i]);
dics.Add(cards[i].GameNum, temp);
}
}
var newdics = dics.OrderByDescending(x => x.Value.Count);
foreach (var item in newdics)
{
result.AddRange(item.Value);
}
return result.ToArray();
}
public static string GetGameNumToString(byte gameNum)
{
string result = "";
if (gameNum <= 0) return result;
if (gameNum >= 3 && gameNum <= 10)
{
result = gameNum.ToString();
}
else
{
switch (gameNum)
{
case 11:
result = "J";
break;
case 12:
result = "Q";
break;
case 13:
result = "K";
break;
case 14:
result = "A";
break;
case 16:
result = "2";
break;
//叫牌中不会有大小王
}
}
return result;
}
}
}

View File

@ -0,0 +1,28 @@
using Newtonsoft.Json;
namespace GameFix
{
public static class JsonHelper
{
public static T DeserializeObject<T>(string jsonData)
{
return JsonConvert.DeserializeObject<T>(jsonData);
}
public static T DeserializeObject<T>(string jsonData, JsonSerializerSettings settings)
{
return JsonConvert.DeserializeObject<T>(jsonData, settings);
}
public static string SerializeObject(object obj)
{
return JsonConvert.SerializeObject(obj);
}
public static string SerializeObject(object obj, JsonSerializerSettings settings)
{
return JsonConvert.SerializeObject(obj, settings);
}
}
}

32
GameFix/Common/Log.cs Normal file
View File

@ -0,0 +1,32 @@
namespace GameFix
{
public class Log
{
public static void Debug(string logStr)
{
#if GameClient
GameFramework.Log.Debug(logStr);
#else
MrWu.Debug.Debug.Log(logStr);
#endif
}
public static void Warning(string logStr)
{
#if GameClient
GameFramework.Log.Warning(logStr);
#else
MrWu.Debug.Debug.Warning(logStr);
#endif
}
public static void Error(string logStr)
{
#if GameClient
GameFramework.Log.Error(logStr);
#else
MrWu.Debug.Debug.Error(logStr);
#endif
}
}
}

View File

@ -0,0 +1,78 @@
namespace GameFix.Mahjong
{
/// <summary>
/// 动作类型
/// </summary>
public enum ActionType
{
/// <summary>
/// 摸牌
/// </summary>
MoPai = 0,
/// <summary>
/// 出牌
/// </summary>
ChuPai = 1,
/// <summary>
/// 吃牌
/// </summary>
ChiPai = 2,
/// <summary>
/// 碰牌
/// </summary>
PengPai = 3,
/// <summary>
/// 杠牌
/// </summary>
GangPai = 4,
/// <summary>
/// 胡牌
/// </summary>
HuPai = 5,
/// <summary>
/// 过牌
/// </summary>
PassPai = 6,
/// <summary>
/// 补牌
/// </summary>
BuPai = 7,
/// <summary>
/// 花杠
/// </summary>
HuaGang = 8,
/// <summary>
/// 出宝牌
/// </summary>
ChuBao = 9,
/// <summary>
/// 掷骰子
/// </summary>
ZhiTouZi = 10,
/// <summary>
/// 亮精
/// </summary>
LiangJing = 11,
/// <summary>
/// 听牌
/// </summary>
TingPai = 12,
/// <summary>
/// 无
/// </summary>
None = 13,
}
}

View File

@ -0,0 +1,217 @@
using MessagePack;
namespace GameFix.Mahjong
{
/// <summary>
/// 杠类型
/// </summary>
public enum GangStyle : byte
{
GangOther = 0,
GangPeng = 1,
GangSelf = 2,
}
/// <summary>
/// 吃类型
/// </summary>
public enum ChiStyle : byte
{
Left = 0,
Middle = 1,
Right = 2
}
/// <summary>
/// 杠信息
/// </summary>
[MessagePackObject]
public class GangInfo
{
/// <summary>
/// 杠的牌
/// </summary>
[Key(0)]
public sbyte Pai;
/// <summary>
/// 0杠别人 1杠碰的牌(明杠), 2杠手里的牌(暗杠) 3单牌补杠
/// </summary>
[Key(1)]
public byte Style;
}
/// <summary>
/// 补牌信息
/// </summary>
public class BuPaiInfo
{
/// <summary>
/// 补的牌
/// </summary>
public sbyte Pai;
/// <summary>
/// 补牌方式
/// </summary>
public byte Style;
}
/// <summary>
/// 吃信息
/// </summary>
[MessagePackObject]
public class ChiInfo
{
/// <summary>
/// 吃信息的数量
/// </summary>
[Key(0)]
public int Cnt;
/// <summary>
/// 吃类型
/// </summary>
[Key(1)]
public int[] Style = new int[3];
/// <summary>
/// 用什么牌可以去吃
/// </summary>
[Key(2)]
public sbyte[,] Pai = new sbyte[3, 2];
}
public enum QueType : byte
{
/// <summary>
/// 吃
/// </summary>
Chi = 0,
/// <summary>
/// 碰
/// </summary>
Peng = 1,
/// <summary>
/// 杠
/// </summary>
Gang = 2,
/// <summary>
/// 补牌
/// </summary>
BuPai = 4,
/// <summary>
/// 花杠
/// </summary>
HuaGang = 5,
/// <summary>
/// 宝牌
/// </summary>
BaoPai = 6,
}
[MessagePackObject]
public class QueInfo
{
/// <summary>
/// 谁提供的牌 -1 表示自己
/// </summary>
[Key(0)]
public int Supply;
/// <summary>
/// 吃碰杠的牌
/// </summary>
[Key(1)]
public sbyte[] Pai = new sbyte[3];
/// <summary>
/// 吃的牌
/// </summary>
[Key(2)]
public sbyte Cp;
/// <summary>
/// 阙的类型
/// </summary>
[Key(3)]
public byte QueStyle;
}
/// <summary>
/// 麻将游戏状态
/// </summary>
public enum MahjongGameState : int
{
/// <summary>
/// 抓牌
/// </summary>
ZhuaPai = 0,
/// <summary>
/// 翻宝 -- 客户端自己处理
/// </summary>
FanBao = 1,
/// <summary>
/// 打牌状态
/// </summary>
DaPai = 2,
/// <summary>
/// 流局
/// </summary>
LiuJu = 3,
/// <summary>
/// 正常结束
/// </summary>
GameOver = 4,
/// <summary>
/// 加买分
/// </summary>
MaiScore = 5,
/// <summary>
/// 掷骰子阶段
/// </summary>
ZhiTouZi = 6,
/// <summary>
/// 亮精阶段
/// </summary>
LiangJing = 7,
/// <summary>
/// 前翻精阶段
/// </summary>
QianFanJing = 8,
/// <summary>
/// 后翻精阶段
/// </summary>
HouFanJing = 9,
/// <summary>
/// 翻宝前阶段
/// </summary>
FanBaoQian = 10,
/// <summary>
/// 翻宝后阶段
/// </summary>
FanBaoHou = 11,
/// <summary>
/// 随精阶段
/// </summary>
SuiJing = 12,
}
}

View File

@ -0,0 +1,20 @@
namespace GameFix.Mahjong
{
public static class MahjongStatic
{
/// <summary>
/// 最大玩家数量
/// </summary>
public const int MaxPlayerCnt = 4;
/// <summary>
/// 最大手牌数量
/// </summary>
public const int MaxShouPaiCnt = 14;
/// <summary>
/// 最大的出牌数量
/// </summary>
public const int MaxPaiOutCnt = 60;
}
}

View File

@ -0,0 +1,105 @@
using MessagePack;
namespace GameFix.Mahjong
{
/// <summary>
/// 操作类型
/// </summary>
public enum OperationType
{
/// <summary>
/// 无操作
/// </summary>
None = 0,
/// <summary>
/// 出牌
/// </summary>
ChuPai = 1,
/// <summary>
/// 过
/// </summary>
Guo = 2,
/// <summary>
/// 吃牌
/// </summary>
ChiPai = 3,
/// <summary>
/// 碰牌
/// </summary>
PengPai = 4,
/// <summary>
/// 杠牌
/// </summary>
GangPai = 5,
/// <summary>
/// 胡牌
/// </summary>
HuPai = 6,
/// <summary>
/// 下一张摸的牌
/// </summary>
NextPai = 7,
/// <summary>
/// 换牌
/// </summary>
HuanPai = 8,
/// <summary>
/// 保存数据
/// </summary>
SaveData = 9,
/// <summary>
/// 买分
/// </summary>
MaiScore = 10,
/// <summary>
/// 杠发财
/// </summary>
GangFaCai = 11,
/// <summary>
/// 补牌
/// </summary>
BuPai = 12,
/// <summary>
/// 发牌结束
/// </summary>
FaPaiOver = 13,
/// <summary>
/// 掷骰子
/// </summary>
ZhiTouZi = 14,
/// <summary>
/// 亮精
/// </summary>
LiangJing = 15,
/// <summary>
/// 听牌
/// </summary>
TingPai = 16,
/// <summary>
/// 选精
/// </summary>
XuanJing = 17,
/// <summary>
/// 换精
/// </summary>
HuanJing = 18,
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace GameFix.Mahjong
{
[Flags]
public enum OperationValue
{
None = 0,
Chi = 1 << 1,
Peng = 1 << 2,
Gang = 1 << 3,
Hu = 1 << 4,
Ting = 1 << 5,
}
}

58
GameFix/GameFix.csproj Normal file
View File

@ -0,0 +1,58 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>GameFix</RootNamespace>
<AssemblyName>GameFix</AssemblyName>
<LangVersion>9.0</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Platforms>AnyCPU</Platforms>
<Configurations>Debug;Release</Configurations>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;Server</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;Server</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MessagePack" />
<PackageReference Include="MessagePack.Annotations" />
<PackageReference Include="MessagePackAnalyzer" PrivateAssets="all" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="Microsoft.NET.StringTools" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="System.Buffers" />
<PackageReference Include="System.Collections.Immutable" />
<PackageReference Include="System.Memory" />
<PackageReference Include="System.Numerics.Vectors" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" />
<PackageReference Include="System.Threading.Tasks.Extensions" />
</ItemGroup>
<!-- <ItemGroup>-->
<!-- <Compile Include="..\..\hjha_Client\Assets\ScriptCode\UnityGame\GamePlay\Common\**\*.cs"-->
<!-- Link="GameFix\%(RecursiveDir)%(Filename)%(Extension)" />-->
<!-- </ItemGroup>-->
<ItemGroup>
<ProjectReference Include="..\MrWu\MrWu.csproj" />
<ProjectReference Include="..\NetWorkMessage\NetWorkMessage.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GameFix")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GameFix")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("AE8D22EB-254B-446A-9C79-BE13E45AFC45")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]