昨晚在一场斗地主直播中,主播放了一个残局上来,谁能破解可以连线。
经多次尝试无果,遂想写个程序暴力破解一下,因为他这显示的是某个游戏的地33关,所以我想这个残局肯定是有解的,不过各位菜鸡水平太差,无法破此残局。
因为要写程序,所以当时就顺便截图了,正好就把这个截图放在了下面。
在写之前,正好可以先问问抖音豆包,看看它能不能解决此问题。
他给的答案我看到第二行就看不下去了,什么鬼,我出单牌,对方出对子,简直太扯淡了,脑瓜子嗡嗡的,行吧,看来还是要靠自己。
程序中的14表示A,20表示2,40表示大王,30表示小王
package com.slf.ddz;
import com.slf.ddz.card.Card;
import com.slf.ddz.card.CardNode;
import com.slf.ddz.card.CardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
public class PlayCard {
private static boolean execute = true;
/**
* 14 20 3 4 5 6 7 8 9 10 11 12 13 30 40
* A 2 3 4 5 6 7 8 9 10 J Q K 小王 大王
*/
public static void main(String[] args) {
Integer[] my1 = {14, 13, 12, 11, 10, 10, 9, 9, 8, 7, 6, 5, 4, 3};
Integer[] his1 = {20, 20, 10, 10, 10, 9, 9, 9};
calcAndPrint(my1, his1, true);
calcAndPrint(my1, his1, false);
Integer[] my2 = {40, 30, 10, 10, 9, 8, 8};
Integer[] his2 = {20, 20, 10, 10, 9, 9};
calcAndPrint(my2, his2, true);
calcAndPrint(my2, his2, false);
}
public static void calcAndPrint(Integer[] my, Integer[] his, boolean concurrent) {
execute = true;
long begin = System.currentTimeMillis();
CardNode cardNode = new CardNode();
Card card = new Card(Arrays.asList(his));
if (concurrent) {
List<CompletableFuture<Void>> list = new ArrayList<>();
List<Card> nextCards = CardType.getNextCards(Arrays.asList(my), null, null);
for (Card nextCard : nextCards) {
list.add(CompletableFuture.runAsync(() -> {
CardNode after = new CardNode(cardNode, nextCard.getPlay());
calc(after, Arrays.asList(his), nextCard, false);
if (after.getColor() == 1) {
cardNode.setColor(1);
execute = false;
}
}));
}
CompletableFuture.allOf(list.toArray(new CompletableFuture[0])).join();
} else {
calc(cardNode, Arrays.asList(my), card, true);
}
print(cardNode, begin, concurrent);
}
private static void print(CardNode node, long begin, boolean concurrent) {
boolean flag = true;
if (node.getColor() == 1) {
System.out.println((concurrent ? "并发" : "串行") + "执行,有解,耗时 " + (System.currentTimeMillis() - begin) + " ms");
while (node.getAfters() != null) {
for (CardNode after : node.getAfters()) {
if (after.getColor() == 1) {
node = after;
System.out.print((flag ? "我" : "对") + "方" + node.getCards() + " ");
flag = !flag;
break;
}
}
}
System.out.println();
} else {
System.out.println((concurrent ? "并发" : "串行") + "执行,无解,耗时 " + (System.currentTimeMillis() - begin) + " ms");
}
System.out.println();
}
public static void calc(CardNode cardNode, List<Integer> now, Card pre, boolean myTurn) {
if (!execute) {
return;
}
if (pre.getCards().isEmpty()) {
cardNode.setColor(myTurn ? 0 : 1);
return;
}
List<Card> nextCards = CardType.getNextCards(now, pre.getPlay(), pre.getType());
for (Card nextCard : nextCards) {
// 出牌
CardNode after = new CardNode(cardNode, nextCard.getPlay());
calc(after, pre.getCards(), nextCard, !myTurn);
if (myTurn && after.getColor() == 1) {
cardNode.setColor(1);
return;
} else if (!myTurn && after.getColor() == 0) {
return;
}
}
if (Objects.nonNull(pre.getPlay())) {
CardNode after = new CardNode(cardNode, new ArrayList<>());
calc(after, pre.getCards(), new Card(now), !myTurn);
}
if (myTurn) {
if (cardNode.getAfters().stream().anyMatch(it -> it.getColor() == 1)) {
cardNode.setColor(1);
}
} else if (cardNode.getAfters().stream().allMatch(it -> it.getColor() == 1)) {
cardNode.setColor(1);
}
}
}
package com.slf.ddz.card;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Card {
private final Integer type;
private final List<Integer> play;
private final List<Integer> cards;
public Card(Integer type, List<Integer> play, Map<Integer, Integer> cardMap) {
this.type = type;
this.play = play;
this.cards = sub(cardMap, play);
}
public List<Integer> getPlay() {
return play;
}
public List<Integer> getCards() {
return cards;
}
public Integer getType() {
return type;
}
private static List<Integer> sub(Map<Integer, Integer> cardsMap, List<Integer> play) {
Map<Integer, Integer> map = new HashMap<>(cardsMap);
for (Integer card : play) {
if (map.get(card) == 1) {
map.remove(card);
} else {
map.put(card, map.get(card) - 1);
}
}
return toCardList(map);
}
private static List<Integer> toCardList(Map<Integer, Integer> map) {
List<Integer> cards = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
for (int i = 0; i < entry.getValue(); i++) {
cards.add(entry.getKey());
}
}
return cards;
}
}
package com.slf.ddz.card;
import java.util.ArrayList;
import java.util.List;
public class CardNode {
private List<CardNode> afters;
private List<Integer> cards;
private int color;
public List<CardNode> getAfters() {
return afters;
}
public void setAfters(List<CardNode> afters) {
this.afters = afters;
}
public List<Integer> getCards() {
return cards;
}
public void setCards(List<Integer> cards) {
this.cards = cards;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public CardNode() {
}
public CardNode(CardNode before, List<Integer> cards) {
this.cards = cards;
if (before.afters == null) {
before.afters = new ArrayList<>();
}
before.afters.add(this);
}
}
package com.slf.ddz.card;
import java.util.*;
import java.util.function.BiFunction;
public class CardType {
private static final Integer SINGLE = 1;
private static final Integer PAIR = 2;
private static final Integer THREE = 3;
private static final Integer THREE_WITH_SINGLE = 4;
private static final Integer THREE_WITH_PAIR = 5;
private static final Integer STRAIGHT = 6;
private static final Integer PAIR_STRAIGHT = 7;
private static final Integer AIRPLANE = 8;
private static final Integer AIRPLANE_WITH_SINGLE = 9;
private static final Integer AIRPLANE_WITH_PAIR = 10;
private static final Integer BOMB = 11;
private static final Integer FOUR_WITH_TWO = 12;
private static final Integer FOUR_WITH_TWO_PAIR = 13;
private static final Map<Integer, BiFunction<Map<Integer, Integer>, List<Integer>, List<Card>>> CARD_TYPE_MAP = new HashMap<>();
static {
CARD_TYPE_MAP.put(SINGLE, CardType::getSingle);
CARD_TYPE_MAP.put(PAIR, CardType::getPair);
CARD_TYPE_MAP.put(THREE, CardType::getThree);
CARD_TYPE_MAP.put(THREE_WITH_SINGLE, CardType::getThreeWithSingle);
CARD_TYPE_MAP.put(THREE_WITH_PAIR, CardType::getThreeWithPair);
CARD_TYPE_MAP.put(STRAIGHT, CardType::getStraight);
CARD_TYPE_MAP.put(PAIR_STRAIGHT, CardType::getPairStraight);
CARD_TYPE_MAP.put(AIRPLANE, CardType::getAirplane);
CARD_TYPE_MAP.put(AIRPLANE_WITH_SINGLE, CardType::getAirplaneWithSingle);
CARD_TYPE_MAP.put(AIRPLANE_WITH_PAIR, CardType::getAirplaneWithPair);
CARD_TYPE_MAP.put(BOMB, CardType::getBomb);
CARD_TYPE_MAP.put(FOUR_WITH_TWO, CardType::getFourWithTwo);
CARD_TYPE_MAP.put(FOUR_WITH_TWO_PAIR, CardType::getFourWithTwoPair);
}
public static List<Card> getNextCards(List<Integer> cards, List<Integer> play, Integer type) {
Map<Integer, Integer> cardMap = toCardMap(cards);
if (Objects.isNull(play)) {
List<Card> all = new ArrayList<>();
for (Map.Entry<Integer, BiFunction<Map<Integer, Integer>, List<Integer>, List<Card>>> entry : CARD_TYPE_MAP.entrySet()) {
all.addAll(entry.getValue().apply(cardMap, null));
}
return all;
}
BiFunction<Map<Integer, Integer>, List<Integer>, List<Card>> function = CARD_TYPE_MAP.get(type);
List<Card> result = function.apply(cardMap, play);
if (!Objects.equals(type, BOMB)) {
result.addAll(getBomb(cardMap, null));
}
return result;
}
/**
* 单
*/
public static List<Card> getSingle(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : cardMap.entrySet()) {
if (play == null || entry.getKey() > play.get(0)) {
List<Integer> one = new ArrayList<>();
one.add(entry.getKey());
playCards.add(new Card(SINGLE, one, cardMap));
}
}
return playCards;
}
/**
* 对
*/
public static List<Card> getPair(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : cardMap.entrySet()) {
if (entry.getValue() > 1 && (play == null || entry.getKey() > play.get(0))) {
List<Integer> two = new ArrayList<>();
two.add(entry.getKey());
two.add(entry.getKey());
playCards.add(new Card(PAIR, two, cardMap));
}
}
return playCards;
}
/**
* 三不带
*/
public static List<Card> getThree(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
List<List<Integer>> threeList = getThreeList(cardMap, play);
for (List<Integer> three : threeList) {
playCards.add(new Card(THREE, three, cardMap));
}
return playCards;
}
/**
* 三带一
*/
public static List<Card> getThreeWithSingle(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
List<List<Integer>> threeList = getThreeList(cardMap, play);
for (List<Integer> three : threeList) {
for (Integer card : cardMap.keySet()) {
if (!Objects.equals(card, three.get(0))) {
List<Integer> threeWithSingle = new ArrayList<>(three);
threeWithSingle.add(card);
playCards.add(new Card(THREE_WITH_SINGLE, threeWithSingle, cardMap));
}
}
}
return playCards;
}
/**
* 三带对
*/
public static List<Card> getThreeWithPair(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
List<List<Integer>> threeList = getThreeList(cardMap, play);
for (List<Integer> three : threeList) {
for (Map.Entry<Integer, Integer> ety : cardMap.entrySet()) {
if (!Objects.equals(ety.getKey(), three.get(0)) && ety.getValue() > 1) {
List<Integer> threeWithPair = new ArrayList<>(three);
threeWithPair.add(ety.getKey());
threeWithPair.add(ety.getKey());
playCards.add(new Card(THREE_WITH_PAIR, threeWithPair, cardMap));
}
}
}
return playCards;
}
private static List<List<Integer>> getThreeList(Map<Integer, Integer> cardMap, List<Integer> play) {
List<List<Integer>> threeList = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : cardMap.entrySet()) {
if (entry.getValue() > 2 && (play == null || entry.getKey() > play.get(0))) {
List<Integer> three = new ArrayList<>();
three.add(entry.getKey());
three.add(entry.getKey());
three.add(entry.getKey());
threeList.add(three);
}
}
return threeList;
}
/**
* 顺子
*/
public static List<Card> getStraight(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : cardMap.entrySet()) {
// 顺子最少5张牌
for (int count = 5; count <= cardMap.size(); count++) {
boolean all = true;
for (int i = entry.getKey(); i < entry.getKey() + count; i++) {
if (!cardMap.containsKey(i)) {
all = false;
break;
}
}
if (all) {
List<Integer> straight = new ArrayList<>();
for (int i = entry.getKey(); i < entry.getKey() + count; i++) {
straight.add(i);
}
if (play == null || (straight.size() == play.size() && straight.get(0) > play.get(0))) {
playCards.add(new Card(STRAIGHT, straight, cardMap));
}
}
}
}
return playCards;
}
/**
* 连对
*/
public static List<Card> getPairStraight(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : cardMap.entrySet()) {
// 连对最少3对
for (int count = 3; count <= cardMap.size(); count++) {
boolean all = true;
for (int i = entry.getKey(); i < entry.getKey() + count; i++) {
if (!cardMap.containsKey(i) || cardMap.get(i) < 2) {
all = false;
break;
}
}
if (all) {
List<Integer> pairStraight = new ArrayList<>();
for (int i = entry.getKey(); i < entry.getKey() + count; i++) {
pairStraight.add(i);
pairStraight.add(i);
}
if (play == null || (pairStraight.size() == play.size() && pairStraight.get(0) > play.get(0))) {
playCards.add(new Card(PAIR_STRAIGHT, pairStraight, cardMap));
}
}
}
}
return playCards;
}
/**
* 飞机不带翅膀
*/
public static List<Card> getAirplane(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
List<List<Integer>> airplaneList = getAirplaneList(cardMap, play);
for (List<Integer> airplane : airplaneList) {
playCards.add(new Card(AIRPLANE, airplane, cardMap));
}
return playCards;
}
/**
* 飞机带单翅膀
*/
public static List<Card> getAirplaneWithSingle(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
List<List<Integer>> airplaneList = getAirplaneList(cardMap, play);
for (List<Integer> airplane : airplaneList) {
List<Integer> singleList = getSingleList(cardMap, airplane);
List<Set<Integer>> combination = combination(singleList.size(), airplaneList.size());
for (Set<Integer> set : combination) {
List<Integer> airplaneWithSingle = new ArrayList<>(airplane);
for (Integer i : set) {
airplaneWithSingle.add(singleList.get(i));
}
playCards.add(new Card(AIRPLANE_WITH_SINGLE, airplaneWithSingle, cardMap));
}
}
return playCards;
}
/**
* 飞机带双翅膀
*/
public static List<Card> getAirplaneWithPair(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
List<List<Integer>> airplaneList = getAirplaneList(cardMap, play);
for (List<Integer> airplane : airplaneList) {
List<Integer> pairList = getPairList(cardMap, airplane);
List<Set<Integer>> combination = combination(pairList.size(), airplaneList.size());
for (Set<Integer> set : combination) {
List<Integer> airplaneWithPair = new ArrayList<>(airplane);
for (Integer i : set) {
airplaneWithPair.add(pairList.get(i));
airplaneWithPair.add(pairList.get(i));
}
if (play == null || (airplane.size() == play.size() && airplane.get(0) > play.get(0))) {
playCards.add(new Card(AIRPLANE_WITH_PAIR, airplaneWithPair, cardMap));
}
}
}
return playCards;
}
private static List<List<Integer>> getAirplaneList(Map<Integer, Integer> cardMap, List<Integer> play) {
List<List<Integer>> airplaneList = new ArrayList<>();
long total = cardMap.values().stream().filter(val -> val > 2).count();
if (total < 2) {
return airplaneList;
}
for (Map.Entry<Integer, Integer> entry : cardMap.entrySet()) {
for (int count = 2; count <= total; count++) {
boolean all = true;
for (int i = entry.getKey(); i < entry.getKey() + count; i++) {
if (!cardMap.containsKey(i) || cardMap.get(i) < 3) {
all = false;
break;
}
}
if (all) {
List<Integer> airplane = new ArrayList<>();
for (int i = entry.getKey(); i < entry.getKey() + count; i++) {
airplane.add(i);
airplane.add(i);
airplane.add(i);
}
if (play == null || (airplane.size() == play.size() && airplane.get(0) > play.get(0))) {
airplaneList.add(airplane);
}
}
}
}
return airplaneList;
}
/**
* 炸弹
*/
public static List<Card> getBomb(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
List<List<Integer>> fourList = getFourList(cardMap, play);
for (List<Integer> four : fourList) {
playCards.add(new Card(BOMB, four, cardMap));
}
// 王炸
if (cardMap.containsKey(30) && cardMap.containsKey(40)) {
List<Integer> bomb = new ArrayList<>();
bomb.add(30);
bomb.add(40);
playCards.add(new Card(BOMB, bomb, cardMap));
}
return playCards;
}
/**
* 四带二
*/
public static List<Card> getFourWithTwo(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
List<List<Integer>> fourList = getFourList(cardMap, play);
for (List<Integer> four : fourList) {
List<Integer> singleList = getSingleList(cardMap, four);
List<Set<Integer>> combination = combination(singleList.size(), 2);
for (Set<Integer> set : combination) {
List<Integer> bombWithTwo = new ArrayList<>(four);
for (Integer i : set) {
bombWithTwo.add(singleList.get(i));
}
if (play == null || four.get(0) > play.get(0)) {
playCards.add(new Card(FOUR_WITH_TWO, bombWithTwo, cardMap));
}
}
}
return playCards;
}
/**
* 四带两对
*/
public static List<Card> getFourWithTwoPair(Map<Integer, Integer> cardMap, List<Integer> play) {
List<Card> playCards = new ArrayList<>();
List<List<Integer>> fourList = getFourList(cardMap, play);
for (List<Integer> four : fourList) {
List<Integer> pairList = getPairList(cardMap, four);
List<Set<Integer>> combination = combination(pairList.size(), 2);
for (Set<Integer> set : combination) {
List<Integer> airplaneWithPair = new ArrayList<>(four);
for (Integer i : set) {
airplaneWithPair.add(pairList.get(i));
airplaneWithPair.add(pairList.get(i));
}
if (play == null || four.get(0) > play.get(0)) {
playCards.add(new Card(FOUR_WITH_TWO_PAIR, airplaneWithPair, cardMap));
}
}
}
return playCards;
}
private static List<List<Integer>> getFourList(Map<Integer, Integer> cardMap, List<Integer> play) {
List<List<Integer>> fourList = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : cardMap.entrySet()) {
if (entry.getValue() > 3 && (play == null || entry.getKey() > play.get(0))) {
List<Integer> four = new ArrayList<>();
four.add(entry.getKey());
four.add(entry.getKey());
four.add(entry.getKey());
four.add(entry.getKey());
fourList.add(four);
}
}
return fourList;
}
private static List<Integer> getSingleList(Map<Integer, Integer> cardMap, List<Integer> cards) {
List<Integer> singleList = new ArrayList<>();
for (Map.Entry<Integer, Integer> ety : cardMap.entrySet()) {
if (!cards.contains(ety.getKey())) {
singleList.add(ety.getKey());
}
}
return singleList;
}
private static List<Integer> getPairList(Map<Integer, Integer> cardMap, List<Integer> cards) {
List<Integer> pairList = new ArrayList<>();
for (Map.Entry<Integer, Integer> ety : cardMap.entrySet()) {
if (!cards.contains(ety.getKey()) && ety.getValue() > 1) {
pairList.add(ety.getKey());
}
}
return pairList;
}
private static Map<Integer, Integer> toCardMap(List<Integer> cards) {
Map<Integer, Integer> map = new HashMap<>();
for (int card : cards) {
map.put(card, map.getOrDefault(card, 0) + 1);
}
return map;
}
/**
* c n k 组合数
*/
public static List<Set<Integer>> combination(int n, int k) {
if (n < k) {
return Collections.emptyList();
}
return combination(n, k, 0);
}
private static List<Set<Integer>> combination(int n, int k, int begin) {
List<Set<Integer>> result = new ArrayList<>();
for (int i = begin; i <= n - k; i++) {
if (k > 1) {
List<Set<Integer>> list = combination(n, k - 1, i + 1);
for (Set<Integer> set : list) {
set.add(i);
}
result.addAll(list);
} else {
Set<Integer> set = new HashSet<>();
set.add(i);
result.add(set);
}
}
return result;
}
}
写完执行程序,得出结果是无解
我就蒙圈了,这是什么情况。
一开始我怀疑是代码写错了,后来经过测试,发现没有错误。
代码的逻辑也比较简单,首先通过递归获取所有的情况,也就是总共有多少种出法,只要满足规则即可,每次出牌对方都可以选择出更大的牌或者不出。
然后至于怎么才算能赢,这里我其实还是卡了一会儿的,现在是知道了所有情况,怎么才算赢呢?总不能说我一直出对方一直不出,也算我赢吧,虽然符合规则,但是对方这明显是放水了。
后来在脑图中找到了思路。根据所有出的牌形成树状结构,也就是程序中定义的CardNode。CardNode有颜色,下图中用线的颜色代替,连接父子元素的线的颜色表示的是字元素的颜色。
为了简化说明,这里举例说明,如以下牌型,我方手牌是 8 10,对方手牌是 9 10,开始计算我方是否存在必胜出法。
按照以上思路,如果我牌是 8 9,对方是 9 10,那么我出 8,对方有三种出法,如果对方出 9 ,我又有两种出法,然后把所有的节点进行标记,首先标记叶子节点,因为是最后一个节点了,最后是谁出的牌就是谁赢,这里把我赢了的话标记为红色,用1表示,对应股票普天同庆的大涨。对方赢了就是股票绿草如茵的下跌,对应的也就是绿色,用0表示。
如果当前轮次是对方出牌,子节点就是我方出牌,只要子节点有一个红的,那么就标记为红,表示我会尽可能赢,反之只要子节点有一个绿的,此节点就是绿的,表示对方会尽可能阻止你赢。
按照这个思路,我只要知道开始节点的颜色,就可以知道有没有必胜的答案了
程序一跑,得出的结果是无解,好吧,主播故意用这种无解的题引流,可恶啊。
不过我既然费半天劲写了,总不能白写,就记录一下吧,万一能帮到什么骚年也不一定。