//出顺子,连对,飞机不带的提示(不能拆的版本) public static List <Card> findStraightNextTwoPlane(List <Card> deck, OutCardStyle preCardStyle, bool canOutBoom, int cardNum) { //按牌相同的数量排序(从大到小)再按牌的大小排序(从大到小) deck.Sort(Card.CompareModeCardNum); List <Card> resCards = new List <Card>(); int pos; //保存最后找的位置 int length = preCardStyle.cardlength; for (pos = deck.Count - 1; pos >= 0 && deck[pos].sameCardNum <= cardNum; pos -= deck[pos].sameCardNum) { if (deck[pos].sameCardNum < cardNum) { continue; } //找到连对:先比它大、它长度最后是对子且是连对且符合要求(小于等于A) if (deck[pos].cardSize > preCardStyle.firstCardSize && pos - length + 1 >= 0 && deck[pos - length + 1].sameCardNum == cardNum && deck[pos - length + 1].cardSize == deck[pos].cardSize + length / cardNum - 1 && deck[pos - length + 1].cardSize <= 14) { while (length-- > 0) { resCards.Add(deck[pos--]); } return(resCards); } } return(canOutBoom == true?findBomb(deck, pos) : resCards); }
//出飞机连对的提示 public static List <Card> findPlaneWithTwo(List <Card> deck, OutCardStyle preCardStyle, bool canOutBoom) { //按牌相同的数量排序(从大到小)再按牌的大小排序(从大到小) deck.Sort(Card.CompareModeCardNum); List <Card> resCards = new List <Card>(); int i; bool isFindTwo = false; int length = preCardStyle.cardlength; int len = length / 5 * 3; int len2 = length / 5 * 2; //先找到足够的对子后, for (i = deck.Count - 1; i >= 0 && deck[i].sameCardNum < 3; i--) { if (deck[i].sameCardNum < 2) { continue; } else if (i - len2 + 1 >= 0 && deck[i - len2 + 1].sameCardNum == 2) { isFindTwo = true; break; } } int j = i; //如果找到对应个数的对子,找大于它的飞机 if (isFindTwo == true) { for (; j >= 0 && deck[j].sameCardNum < 4; j -= deck[j].sameCardNum) { if (deck[j].sameCardNum < 3) { continue; } //找到飞机不带:先比它大、它长度最后是三个头且是飞机且符合要求(小于等于A) if (deck[j].cardSize > preCardStyle.firstCardSize && j - len + 1 >= 0 && deck[j - len + 1].sameCardNum == 3 && deck[j - len + 1].cardSize == deck[j].cardSize + len / 3 - 1 && deck[j - len + 1].cardSize <= 14) { for (int k = 0; k < len; k++) { resCards.Add(deck[j--]); } for (int k = 0; k < len2; k++) { resCards.Add(deck[i--]); } //返回结果 return(resCards); } } } return(canOutBoom == true?findBomb(deck, j) : resCards); }
//让后面的农民跑 private static List <Card> letFarmerGo(List <Card>[] peoplesCard, int whoOut, int nowPerson, int whoCanOut) { List <Card> curOutCards = nextRemindCard[0]; //如果是自己出牌(必须要出),想办法放农民跑 if (whoOut == nowPerson) { OutCardStyle nextCardStyle = OutCardStyle.judgeCardStyle(nextRemindCard[whoCanOut]); //如果后面的农民出的是炸弹或者是四大天王,自己就随便出 if (nextCardStyle.outCardStyleEnum == OutCardStyleEnum.BOMB || nextCardStyle.outCardStyleEnum == OutCardStyleEnum.FOUR_GHOST) { } //如果是单支或者是对子,拆任意牌,放农民走 else if (nextCardStyle.outCardStyleEnum == OutCardStyleEnum.ONE || nextCardStyle.outCardStyleEnum == OutCardStyleEnum.TWO) { List <Card> tempOutCards = new List <Card>(); switch (nextCardStyle.outCardStyleEnum) { case OutCardStyleEnum.ONE: tempOutCards = CrushPreCard.findMinOneTwo(peoplesCard[nowPerson], 1); break; case OutCardStyleEnum.TWO: tempOutCards = CrushPreCard.findMinOneTwo(peoplesCard[nowPerson], 2); break; } if (tempOutCards.Count > 0 && tempOutCards[0].cardSize < nextCardStyle.firstCardSize) { curOutCards = tempOutCards; } } //如果是其他类型的牌 else { //找同类型自己能出的最小的牌(可以拆)(利用玩家的提示) People people = new People(); people.deck = peoplesCard[nowPerson]; people.htStyle = nextCardStyle.outCardStyleEnum; PlayerRemindCard.peopleFirstHint(people, true); //判断当前出了以后,后面的农民能不能跑 OutCardStyle myCardOutStyle = OutCardStyle.judgeCardStyle(people.htCards); //不能出证明放不了,就随便出 if (myCardOutStyle.outCardStyleEnum == nextCardStyle.outCardStyleEnum && myCardOutStyle.firstCardSize < nextCardStyle.firstCardSize) { curOutCards = people.htCards; } } } //如果可以不出,则不出 else { curOutCards.Clear(); } return(curOutCards); }
//出炸弹的提示(打别人的炸弹)(尽量找最小) public static List <Card> findBomb(List <Card> deck, OutCardStyle preCardStyle) { //按牌相同的数量排序(从大到小)再按牌的大小排序(从大到小) deck.Sort(Card.CompareModeCardNum); List <Card> resCards = new List <Card>(); for (int i = deck.Count - 1; i >= 0; i -= deck[i].sameCardNum) { //我出炸弹 if (deck[i].sameCardNum == preCardStyle.cardlength && deck[i].cardSize > preCardStyle.firstCardSize || deck[i].sameCardNum > preCardStyle.cardlength) { int cardNum = deck[i].sameCardNum; while (cardNum-- > 0) { resCards.Add(deck[i--]); } //如果发现则返回 return(resCards); } } return(findFourGoust(deck)); }
//出单支,对子,三不带的提示,不可以拆炸弹,但可以拆2,玩家出牌可以拆任意的牌(尽量找最小) public static List <Card> findOneTwoThree(List <Card> deck, OutCardStyle preCardStyle, bool canOutBoom, bool canSplit, int cardNum) { //按牌相同的数量排序(从大到小)再按牌的大小排序(从大到小) deck.Sort(Card.CompareModeCardNum); List <Card> resCards = new List <Card>(); //先从对子里面找,根据情况能不能拆,不拆4个头,(拆不是4个头的炸还是炸) int pos; //保存最后找的位置 for (pos = deck.Count - 1; pos >= 0 && deck[pos].sameCardNum < 4; pos -= deck[pos].sameCardNum) { //找到对子则返回 if (deck[pos].cardSize > preCardStyle.firstCardSize) { if (deck[pos].sameCardNum == cardNum) { while (cardNum-- > 0) { resCards.Add(deck[pos--]); } return(resCards); } else if (deck[pos].sameCardNum > cardNum) { //如果处于自己出牌的状态(不拆其他牌) if (canOutBoom == false) { break; } //如果处于打别人牌状态(可以拆分) else { //如果可以拆分(玩家提示,无论拆什么都行(不拆炸弹)) // 如果是电脑出牌,找2,小王,大王拆 // 或者是打单支只剩对子,打对子只剩三个头 if (canSplit == true || deck[pos].cardSize >= 15 || deck.Count <= cardNum + 1) { while (cardNum-- > 0) { resCards.Add(deck[pos--]); } return(resCards); } } } } } return(canOutBoom == true?findBomb(deck, pos) : resCards); }
//玩家任意出牌的游戏提示,记录上次找的牌 private static List <Card> remindCard(List <Card> deck, List <Card> prevCard) { //判断上家的卡组样式 OutCardStyle preOutCardStyle = OutCardStyle.judgeCardStyle(prevCard); //如果想出什么牌就出什么牌 if (preOutCardStyle.outCardStyleEnum == OutCardStyleEnum.CANT_OUT) { return(Robort_Free_Remind(deck)); } //要不住,提示炸弹 else { return(CrushPreCard.crushPreCard(deck, preOutCardStyle, true, false)); } }
//玩家任意出牌的游戏提示,记录上次找的牌 public static void remindCard(People people, List <Card> prevCard, bool canOutBoom) { //判断上家的卡组样式 OutCardStyle preOutCardStyle = OutCardStyle.judgeCardStyle(prevCard); //如果想出什么牌就出什么牌 if (preOutCardStyle.outCardStyleEnum == OutCardStyleEnum.CANT_OUT) { people.htCards = new List <Card>(); peopleFirstHint(people, true); } //打上一家出的牌(如果是打自己出的牌,canOutBoom=false) else { people.htCards = CrushPreCard.crushPreCard(people.deck, preOutCardStyle, canOutBoom, true); } }
//农民打别人的牌//打农民不炸 > 单牌守门 > 后面的能不能要,能要不出炸弹 private static List <Card> farmerCrushCard(List <Card>[] people, int whosland, int whoOut, int nowPerson, List <Card> prevCard) { //landlord:谁是地主 nowPerson:谁要出牌 whoOut:上家出牌的是谁 //上家出牌的是自己人而且我只能出炸弹,则不出牌 if (OutCardStyle.isBomb(nextRemindCard[0]) != 0 && whoOut != whosland) { return(new List <Card>()); //返回空值 } //如果下家是地主,且上家出的牌是单牌,则需要守门 if ((nowPerson + 1) % 4 == whosland) { if (prevCard.Count == 1) { return(robortSingleDefend(people[nowPerson], prevCard)); } } //判断自己是否一定要出炸弹 int i, j; for (i = nowPerson, j = 0; i != whosland; i = (i + 1) % 4, j++) { //如果该家出的是普通牌 if (OutCardStyle.isBomb(nextRemindCard[j]) == 0 && nextRemindCard[j].Count != 0) { break; } } //如果我家出的是普通牌,或者没有一家出的不是普通牌 if (i == nowPerson || i == whosland) { return(nextRemindCard[0]); } //后面有农民可以要住,我就不用炸 else { return(new List <Card>()); //返回空值 } }
public static List <Card> crushPreCard(List <Card> deck, OutCardStyle preOutCardStyle, bool canOutBoom, bool canSplit) { List <Card> reminderList = new List <Card>(); //如果卡组比本身的牌少,不考虑 if (deck.Count < preOutCardStyle.cardlength) { return(reminderList); } //上家出什么调用相应的提示 switch (preOutCardStyle.outCardStyleEnum) { case OutCardStyleEnum.FOUR_GHOST: //上家出四大天王 break; case OutCardStyleEnum.BOMB: //上家出炸弹 reminderList = findBomb(deck, preOutCardStyle); break; case OutCardStyleEnum.ONE: //上家出单牌 reminderList = findOneTwoThree(deck, preOutCardStyle, canOutBoom, canSplit, 1); break; case OutCardStyleEnum.TWO: //上家出对子 reminderList = findOneTwoThree(deck, preOutCardStyle, canOutBoom, canSplit, 2); break; case OutCardStyleEnum.THREE: //上家出三不带 reminderList = findOneTwoThree(deck, preOutCardStyle, canOutBoom, canSplit, 3); break; case OutCardStyleEnum.THREE_TAKE_TWO: //上家出三带二 reminderList = findThreeWithTwo(deck, preOutCardStyle, canOutBoom, canSplit); break; case OutCardStyleEnum.NEXT_TWO: //上家出连对 //如果是玩家自己出牌的提示,不拆 if (canSplit == true && canOutBoom == false) { reminderList = findStraghitOrNextTwoCanSplit(deck, preOutCardStyle, canOutBoom, 2, false); } else { reminderList = findStraghitOrNextTwoCanSplit(deck, preOutCardStyle, canOutBoom, 2, canSplit); } break; case OutCardStyleEnum.STRAIGHT: //上家出顺子 //如果是玩家自己出牌的提示,不拆 if (canSplit == true && canOutBoom == false) { reminderList = findStraghitOrNextTwoCanSplit(deck, preOutCardStyle, canOutBoom, 1, false); } else { reminderList = findStraghitOrNextTwoCanSplit(deck, preOutCardStyle, canOutBoom, 1, canSplit); } break; case OutCardStyleEnum.PLANE: //上家出飞机不带 reminderList = findStraightNextTwoPlane(deck, preOutCardStyle, canOutBoom, 3); break; case OutCardStyleEnum.PLANE_TAKE_TWO: //上家出飞机带两对 reminderList = findPlaneWithTwo(deck, preOutCardStyle, canOutBoom); break; } return(reminderList); }
//出顺子或连对的提示(可拆版本)//必须先按牌从大到小排序 public static List <Card> findStraghitOrNextTwoCanSplit(List <Card> deck, OutCardStyle preCardStyle, bool canOutBoom, int oneOrTwo, bool canSplit) { //按牌的大小排序(从大到小) deck.Sort(Card.CompareModeCardSize); List <Card> resCards = new List <Card>(); int length = preCardStyle.cardlength / oneOrTwo; //对应要出的牌的长度 int i = deck.Count - 1; for (; i >= 0; i -= deck[i].sameCardNum) { if (deck[i].cardSize > preCardStyle.firstCardSize && deck[i].sameCardNum >= oneOrTwo) { int j = i, nextJ = i; int splitNum = 0; //拆成单支的个数 int curLen = 0; //当前长度 do { //判断有没有拆炸弹(如果拆的炸弹,拆完之后还是炸弹不算) if (deck[nextJ].sameCardNum >= 4 && deck[nextJ].sameCardNum - oneOrTwo < 4) { break; } //判断是否拆了不是顺子或三个(拆了之后变成单支了) if (deck[nextJ].sameCardNum == oneOrTwo + 1) { splitNum++; } //将结果保存起来 for (int k = 0; k < oneOrTwo; k++) { resCards.Add(deck[nextJ - k]); } curLen++; //长度增加 j = nextJ; //找下一个 nextJ = j - deck[j].sameCardNum; //找到足够数量的顺子或连对,数量超过cardnum,长度要够上一家出的牌,要连在一起,最高是A } while (nextJ >= 0 && deck[nextJ].sameCardNum >= oneOrTwo && curLen < length && deck[j].cardSize == deck[nextJ].cardSize - 1 && deck[nextJ].cardSize <= 14); //如果找到 if (curLen == length) { //根据拆的个数的长度,判断是否让拆 //如果len=5,最多可以拆2个,len=6,可拆2,len=7,可拆3 //如果是可以随便拆,或者电脑可以拆的版本跳出,否则找下一种(电脑可拆) if (length >= splitNum * 2 + 1 || canSplit == true) { break; } } //如果没找到,i直接从j开始,因为它不连续,或者中间有炸弹 i = j; resCards.Clear(); } } //按牌相同的数量排序(从大到小)再按牌的大小排序(从大到小) deck.Sort(Card.CompareModeCardNum); return(canOutBoom == true?findBomb(deck, deck.Count - 1) : resCards); }
//出三带二的提示//先塞3个,再塞2个//如果没有对子,可以从比当前三个头小的对子中拆对子出 public static List <Card> findThreeWithTwo(List <Card> deck, OutCardStyle preCardStyle, bool canOutBoom, bool canSplit) { //按牌相同的数量排序(从大到小)再按牌的大小排序(从大到小) deck.Sort(Card.CompareModeCardNum); List <Card> resCards = new List <Card>(); int twoPos, threePos, twoInThreePos; bool isFindTwo = false; //在对子中有没有找到对子 bool isInThreeFindTwo = false; //在三个个头中有没有找到对子 //先找对子 for (twoPos = deck.Count - 1; twoPos >= 0; twoPos--) { if (deck[twoPos].sameCardNum < 2) { continue; } else if (deck[twoPos].sameCardNum > 2) { break; } else { //找到对子 isFindTwo = true; break; } } twoInThreePos = twoPos; //保存未找到或找到的最后的位置 //如果没有找到正好为两张的对子,就在三张里面找对子 if (canSplit == true && !isFindTwo) { for (; twoInThreePos >= 0; twoInThreePos -= deck[twoInThreePos].sameCardNum) { if (deck[twoInThreePos].sameCardNum < 3) { continue; } else if (deck[twoInThreePos].sameCardNum > 3) { break; } else { //找到对子 isInThreeFindTwo = true; break; } } } threePos = twoInThreePos; //保存未找到或找到的最后的位置 //找对应的三个头 if (isFindTwo || isInThreeFindTwo) { for (; threePos >= 0; threePos -= deck[threePos].sameCardNum) { if (deck[threePos].sameCardNum < 3) { continue; } if (deck[threePos].sameCardNum > 3) { break; } //找到三个头 if (deck[threePos].cardSize > preCardStyle.firstCardSize) { for (int k = 0; k < 3; k++) { resCards.Add(deck[threePos--]); } for (int k = 0; k < 2; k++) { resCards.Add(deck[twoPos--]); } return(resCards); } } } return(canOutBoom == true?findBomb(deck, threePos) : resCards); }
//判断出牌的种类 public static OutCardStyle judgeCardStyle(List <Card> cards) { //0出错,1单支,2对子,3三不带,4三带二,5连对,6顺子,7飞机不带,8飞机带两对,9炸弹,10四大天王 OutCardStyle cardstyle = new OutCardStyle(); if (cards == null || cards.Count == 0) { cardstyle.outCardStyleEnum = OutCardStyleEnum.CANT_OUT; return(cardstyle); } //保存出的牌的长度 cardstyle.cardlength = cards.Count; //先根据牌的数量判断单支,对子,三个头 switch (cards.Count) { case 1: cardstyle.outCardStyleEnum = OutCardStyleEnum.ONE; cardstyle.firstCardSize = cards[0].cardSize; return(cardstyle); case 2: cardstyle.firstCardSize = isTwo(cards); if (cardstyle.firstCardSize > 0) { cardstyle.outCardStyleEnum = OutCardStyleEnum.TWO; } return(cardstyle); case 3: cardstyle.firstCardSize = isThree(cards); if (cardstyle.firstCardSize > 0) { cardstyle.outCardStyleEnum = OutCardStyleEnum.THREE; } return(cardstyle); } //判断三带二 cardstyle.firstCardSize = isThreeTakeTwo(cards); if (cardstyle.firstCardSize > 0) { cardstyle.outCardStyleEnum = OutCardStyleEnum.THREE_TAKE_TWO; return(cardstyle); } //判断连对 cardstyle.firstCardSize = isNextTwo(cards); if (cardstyle.firstCardSize > 0) { cardstyle.outCardStyleEnum = OutCardStyleEnum.NEXT_TWO; return(cardstyle); } //判断顺子 cardstyle.firstCardSize = isStraight(cards); if (cardstyle.firstCardSize > 0) { cardstyle.outCardStyleEnum = OutCardStyleEnum.STRAIGHT; return(cardstyle); } //判断飞机不带 cardstyle.firstCardSize = isPlane(cards); if (cardstyle.firstCardSize > 0) { cardstyle.outCardStyleEnum = OutCardStyleEnum.PLANE; return(cardstyle); } //判断飞机带两对 cardstyle.firstCardSize = isPlaneWithTwo(cards); if (cardstyle.firstCardSize > 0) { cardstyle.outCardStyleEnum = OutCardStyleEnum.PLANE_TAKE_TWO; return(cardstyle); } //判断炸弹 cardstyle.firstCardSize = isBomb(cards); if (cardstyle.firstCardSize > 0) { cardstyle.outCardStyleEnum = OutCardStyleEnum.BOMB; return(cardstyle); } //判断四大天王 if (isFourGhost(cards)) { cardstyle.outCardStyleEnum = OutCardStyleEnum.FOUR_GHOST; return(cardstyle); } //什么类型都不是 return(cardstyle); }
//玩家想出什么牌出什么牌的提示 // 按各种可能性提示,第i+1次提示比第i次提示要不同类型大,要不提示下一种类型 // 算法思想,伪造一个cardStyle,如果当前玩家能打,则可以出 public static void peopleFirstHint(People people, bool canSplit) { List <Card> deck = people.deck; int length = people.deck.Count; OutCardStyle createOutCardStyle; //伪造一个outCardStyle,判断其能否出相应的牌 do { switch (people.htStyle) { //如果是第一次提示(大的开始提示) case OutCardStyleEnum.CANT_OUT: case OutCardStyleEnum.PLANE_TAKE_TWO: //飞机带几对(按最长的长度,最小的(不存在)构造,再依次降低长度,直到找到) if (length >= 10) { for (int cardLen = length / 5 * 5; cardLen >= 10 && people.htCards.Count == 0; cardLen -= 5) { createOutCardStyle = new OutCardStyle(OutCardStyleEnum.PLANE_TAKE_TWO, 0, cardLen); people.htCards = CrushPreCard.crushPreCard(deck, createOutCardStyle, false, canSplit); } } people.htStyle = OutCardStyleEnum.PLANE; break; case OutCardStyleEnum.PLANE: //飞机不带 for (int cardLen = length / 3 * 3; cardLen >= 6 && people.htCards.Count == 0; cardLen -= 3) { createOutCardStyle = new OutCardStyle(OutCardStyleEnum.PLANE, 0, cardLen); people.htCards = CrushPreCard.crushPreCard(deck, createOutCardStyle, false, canSplit); } people.htStyle = OutCardStyleEnum.NEXT_TWO; break; case OutCardStyleEnum.NEXT_TWO: //连对,对3-对A共24张 for (int i = Math.Min(24, length / 2 * 2); i >= 6 && people.htCards.Count == 0; i -= 2) { createOutCardStyle = new OutCardStyle(OutCardStyleEnum.NEXT_TWO, 0, i); people.htCards = CrushPreCard.crushPreCard(deck, createOutCardStyle, false, canSplit); } people.htStyle = OutCardStyleEnum.STRAIGHT; break; case OutCardStyleEnum.STRAIGHT: //顺子,3-A共12张 for (int cardLen = Math.Min(12, length); cardLen >= 5 && people.htCards.Count == 0; cardLen--) { createOutCardStyle = new OutCardStyle(OutCardStyleEnum.STRAIGHT, 0, cardLen); people.htCards = CrushPreCard.crushPreCard(deck, createOutCardStyle, false, canSplit); } people.htStyle = OutCardStyleEnum.THREE_TAKE_TWO; break; case OutCardStyleEnum.THREE_TAKE_TWO: createOutCardStyle = new OutCardStyle(OutCardStyleEnum.THREE_TAKE_TWO, 0, 5); people.htCards = CrushPreCard.crushPreCard(deck, createOutCardStyle, false, canSplit); people.htStyle = OutCardStyleEnum.THREE; break; case OutCardStyleEnum.THREE: createOutCardStyle = new OutCardStyle(OutCardStyleEnum.THREE, 0, 3); people.htCards = CrushPreCard.crushPreCard(deck, createOutCardStyle, false, canSplit); people.htStyle = OutCardStyleEnum.TWO; break; case OutCardStyleEnum.TWO: createOutCardStyle = new OutCardStyle(OutCardStyleEnum.TWO, 0, 2); people.htCards = CrushPreCard.crushPreCard(deck, createOutCardStyle, false, canSplit); people.htStyle = OutCardStyleEnum.ONE; break; case OutCardStyleEnum.ONE: createOutCardStyle = new OutCardStyle(OutCardStyleEnum.ONE, 0, 1); people.htCards = CrushPreCard.crushPreCard(deck, createOutCardStyle, false, canSplit); people.htStyle = OutCardStyleEnum.BOMB; break; case OutCardStyleEnum.BOMB: for (int i = 8; i >= 4; i--) { createOutCardStyle = new OutCardStyle(OutCardStyleEnum.BOMB, 0, i); people.htCards = CrushPreCard.crushPreCard(deck, createOutCardStyle, false, canSplit); } people.htStyle = OutCardStyleEnum.FOUR_GHOST; break; case OutCardStyleEnum.FOUR_GHOST: if (OutCardStyle.isFourGhost(people.deck) == true) { people.htCards = people.deck; } people.htStyle = OutCardStyleEnum.CANT_OUT; break; } } while (people.htCards.Count == 0 && people.htStyle != OutCardStyleEnum.CANT_OUT); }