feyerobot/libs/robot_common/src/main/java/taurus/util/ChangshaWinSplitCard.java

819 lines
27 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package taurus.util;
import com.game.Util;
import java.util.*;
import java.util.stream.Collectors;
public class ChangshaWinSplitCard {
// 统计牌数
public static int[][] countTiles(List<Integer> cardInHand) {
int[][] counts = new int[5][10]; // 类型×值
for (Integer card : cardInHand) {
if (card == 0) {
continue;
}
counts[card / 100 - 1][card % 100]++;
}
return counts;
}
// 检测不带精湖
public static int checkNormalHu(List<Integer> cardInHand, Map<String, Object> map) {
String info = "";
int[][] counts = countTiles(cardInHand);
int group = 0;
// 尝试找将牌
for (int type = 0; type < 5; type++) {
int max = 9;
for (int value = 1; value <= max; value++) {
if (counts[type][value] >= 2 && (value == 2 || value == 5 || value == 8)) {
// 找到可能的将牌
counts[type][value] -= 2;
group = checkMelds(counts, cardInHand.size() / 3, map);
if (group == 0) { // 剩余12张牌需要组成4个顺子或刻子
counts[type][value] += 2;
return 0;
}
counts[type][value] += 2;
} else {
group = checkMelds(counts, (cardInHand.size() / 3) + 1, map);
if (group == 0) { // 剩余12张牌需要组成4个顺子或刻子
counts[type][value] += 2;
return 0;
}
}
}
}
List<Integer> cardResiue = (List<Integer>) map.get("cardResiue");
for (int i = 0; i < cardResiue.size() - 1; i++) {
if (cardResiue.get(i).equals(cardResiue.get(i + 1)) && (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8)) {
cardResiue.remove(i);
cardResiue.remove(i);
break;
}
}
haveBetween(cardResiue, cardInHand);
System.out.println("可以打出的牌:" + cardResiue);
System.out.println("总共差几手牌:" + map.get("remainingMelds"));
return group;
}
public static void haveBetween(List<Integer> cards, List<Integer> cardInHand) {
if (cards.size() <= 1 || cardInHand.size() <= 1) {
return;
}
// 创建要删除的牌列表
Set<Integer> toRemove = new HashSet<>();
// 遍历手牌中的每一张牌
for (int handCard : cardInHand) {
// 检查cards中是否有与手牌形成靠章的牌
for (int card : cards) {
// 定义靠章规则:相同、相邻或隔一张
int diff = Math.abs(card - handCard);
if (diff == 1 || diff == 2) {
toRemove.add(card);
}
}
}
// 只有当cards中还有多于1张牌时才执行删除
//if (toRemove.size()- cards.size() > 1){
if(cards.size()-toRemove.size()>0){
cards.removeAll(toRemove);
}
// 可选:打印信息
if (!toRemove.isEmpty()) {
System.out.println("删除的牌: " + toRemove);
System.out.println("cards剩余: " + cards);
}
}
// 递归检查是否可以组成顺子或刻子
public static int checkMelds(int[][] counts, int remainingMelds, Map<String, Object> map) {
if (remainingMelds == 0) {
return 0;
}
// 优先检查刻子
for (int type = 0; type < 5; type++) {
int max = 9;
for (int value = 1; value <= max; value++) {
if (counts[type][value] >= 3) {
counts[type][value] -= 3;
if (checkMelds(counts, remainingMelds - 1, map) == 0) {
counts[type][value] += 3;
return 0;
}
counts[type][value] += 3;
}
}
}
// 检查顺子
for (int type = 0; type < 5; type++) {
int max = 7;
for (int value = 1; value <= max; value++) {
if (counts[type][value] >= 1 && counts[type][value + 1] >= 1 && counts[type][value + 2] >= 1) {
counts[type][value]--;
counts[type][value + 1]--;
counts[type][value + 2]--;
if (checkMelds(counts, remainingMelds - 1, map) == 0) {
counts[type][value]++;
counts[type][value + 1]++;
counts[type][value + 2]++;
return 0;
}
counts[type][value]++;
counts[type][value + 1]++;
counts[type][value + 2]++;
}
}
}
// 判断孤章
List<Integer> cardResiue = convertCountToCards(counts);
// System.out.println("只差几手牌" + remainingMelds + "," + cardResiue);
if (cardResiue.size()!=0) {
//优先保留连续张
removeConsecutivePairs1(cardResiue);
}
if (cardResiue.size()!=0) {
//第二优先保留对子
removeConsecutivePairsTwo(cardResiue);
}
//最后保留坎张
//如果坎张,为空则取边张
if (cardResiue.size()!=0) {
List<Integer> tmp3 = new ArrayList<>();
tmp3.addAll(cardResiue);
removeConsecutivePairs2(cardResiue);
if (cardResiue.size() == 0) {
//取边站
cardResiue = qubianzhang(tmp3);
}
}
if (remainingMelds == 0) {
map.put("remainingMelds", 0);
map.put("cardResiue", "已经胡牌");
}
if (map.get("remainingMelds") == null) {
map.put("remainingMelds", remainingMelds);
}
if (map.get("cardResiue") == null) {
map.put("cardResiue", cardResiue);
}
for (int i = 0; i < cardResiue.size() - 1; i++) {
if (cardResiue.get(i).equals(cardResiue.get(i + 1)) && (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8)) {
cardResiue.remove(i);
cardResiue.remove(i);
if (remainingMelds >= 2) {
remainingMelds--;
}
break;
}
}
if (Integer.parseInt(map.get("remainingMelds").toString()) >= remainingMelds && cardResiue.size() > 0) {
// 如果没有258做将则需要进入这里判断
if (remainingMelds == 1 && cardResiue.size() == 2) {
for (int i = 0; i < cardResiue.size(); i++) {
int diff = Math.abs(cardResiue.get(0) - cardResiue.get(1));
if (diff>1) {
map.put("remainingMelds", remainingMelds + 1);// 删除符合258做将的牌if (cardResiue.size() > 1) {cardResiue.remove(i);}map.put("cardResiue", cardResiue);
}else {
if (cardResiue.get(i) % 100 == 2 || cardResiue.get(i) % 100 == 5 || cardResiue.get(i) % 100 == 8) {
// 删除符合258做将的牌
if (cardResiue.size() > 1) {
cardResiue.remove(i);
}
map.put("remainingMelds", remainingMelds);
map.put("cardResiue", cardResiue);
} else {
map.put("remainingMelds", remainingMelds);
map.put("cardResiue", cardResiue);
}
}
}
} else if (remainingMelds == 1) {
map.put("remainingMelds", remainingMelds);
map.put("cardResiue", cardResiue);
}
}
return remainingMelds;
}
public static void removeConsecutivePairsTwo(List<Integer> cards) {
if (cards == null || cards.size() < 2) {
return;
}
// 先排序
List<Integer> sorted = new ArrayList<>(cards);
Collections.sort(sorted);
List<Integer> result = new ArrayList<>();
int i = 0;
while (i < sorted.size()) {
// boolean isleft = sorted.get(i) % 100 == 1 || sorted.get(i) % 100 == 8;
if (i < sorted.size() - 1 && sorted.get(i + 1) - sorted.get(i) == 0) {
// 跳过对子
i += 2;
} else {
// 保留这张牌
result.add(sorted.get(i));
i++;
}
}
cards.clear();
cards.addAll(result);
}
// 辅助方法
public static void removeConsecutivePairs1(List<Integer> cards) {
if (cards == null || cards.size() < 2) {
return;
}
// 先排序
List<Integer> sorted = new ArrayList<>(cards);
Collections.sort(sorted);
List<Integer> result = new ArrayList<>();
int i = 0;
while (i < sorted.size()) {
boolean isleft = sorted.get(i) % 100 == 1 || sorted.get(i) % 100 == 8;
if (!isleft && i < sorted.size() - 1 && sorted.get(i + 1) - sorted.get(i) == 1) {
// 跳过这对连续牌
i += 2;
} else {
// 保留这张牌
result.add(sorted.get(i));
i++;
}
}
cards.clear();
cards.addAll(result);
}
public static List<Integer> qubianzhang(List<Integer> cards) {
List<Integer> result = new ArrayList<>();
for (int i = 0; i < cards.size(); i++) {
if (cards.get(i)%100 == 1||cards.get(i)%100 == 9) {
result.add(cards.get(i));
}
}
return result;
}
public static void removeConsecutivePairs2(List<Integer> cards) {
if (cards == null || cards.size() < 2) {
return;
}
// 先排序
List<Integer> sorted = new ArrayList<>(cards);
Collections.sort(sorted);
// 使用布尔数组标记要删除的牌
boolean[] toRemove = new boolean[sorted.size()];
// 找出所有隔张相差2的牌对如1和3、2和4等
for (int i = 0; i < sorted.size(); i++) {
if (!toRemove[i]) { // 如果这张牌还没被标记删除
for (int j = i + 1; j < sorted.size(); j++) {
if (!toRemove[j]) { // 如果这张牌还没被标记删除
int diff = sorted.get(j) - sorted.get(i);
if (diff == 2 && sorted.size()>2) {
// 找到隔张,标记删除
toRemove[i] = true;
toRemove[j] = true;
if (j + 1 < sorted.size()) {
if (sorted.get(j + 1) % 100 == 2 || sorted.get(j + 1) % 100 == 5
|| sorted.get(j + 1) % 100 == 6) {
toRemove[i] = false;
toRemove[j] = false;
}
}
break; // 找到一对就跳出内层循环
} else if (diff > 2) {
// 由于已排序,差值只会越来越大
break;
}
}
}
}
}
// 构建结果列表(保留未标记删除的牌)
List<Integer> result = new ArrayList<>();
for (int i = 0; i < sorted.size(); i++) {
if (!toRemove[i]) {
result.add(sorted.get(i));
}
}
cards.clear();
cards.addAll(result);
}
// 从二维数组counts还原到手牌列表
private static List<Integer> convertCountToCards(int[][] counts) {
List<Integer> cardInHand = new ArrayList<>();
for (int suitIdx = 0; suitIdx < counts.length; suitIdx++) {
int suit = suitIdx + 1;
for (int value = 1; value < counts[suitIdx].length; value++) {
int count = counts[suitIdx][value];
for (int i = 0; i < count; i++) {
int card = suit * 100 + value;
cardInHand.add(card);
}
}
}
return cardInHand;
}
public static List<Integer> checktingpai(List<Integer> cardhand) {
List<Integer> tpcards = new ArrayList<>();
List<Integer> tmphc = cardhand;
for (int i = 0; i < cardhand.size(); i++) {
int tmpcard = tmphc.get(0);
tmphc.remove(0);
for (int j = 101; j <= 109; j++) {
WinCard win = new WinCard(tmphc, j);
if (win.tryWin()) {
if (!tpcards.contains(tmpcard)) {
tpcards.add(tmpcard);
}
}
}
for (int j = 201; j <= 209; j++) {
WinCard win = new WinCard(tmphc, j);
if (win.tryWin()) {
if (!tpcards.contains(tmpcard)) {
tpcards.add(tmpcard);
}
}
}
tmphc.add(tmpcard);
}
return tpcards;
}
public static boolean isJiangPai(int card){
if (card%100==2||card%100==5||card%100==8){
return true;
}
return false;
}
public static int checkduijiang(List<Integer> cardInHand,Map<String,List<Integer>> map) {
Map<Integer, Integer> countMap = new HashMap<>();
for (Integer item : cardInHand) {
countMap.put(item, countMap.getOrDefault(item, 0) + 1);
}
int jiangnum = 0;
for (int key : countMap.keySet()) {
if (isJiangPai(key)&&countMap.get(key)>=2){
jiangnum++;
List<Integer> tmpI = new ArrayList<>();
tmpI.add(key);
map.put("jiangcard", tmpI);
}
}
return jiangnum;
}
// 分析最优出牌
public static List<Integer> analyzeBestDiscard(List<Integer> cardInHand) {
//听牌
//返回要打的牌,打后可以听牌
List<Integer> checktingpai = checktingpai(cardInHand);
System.out.println("打出这种牌后可以听牌 " + checktingpai);
Map<String, Object> map = new HashMap<>();
Map<String, List<Integer>> jmap = new HashMap<>();//对将map
//先定将,再做分析
/* int jiangnum = checkduijiang(cardInHand,jmap);
System.out.println(jmap.get("jiangcard").get(0));
if (jiangnum==1){
//如果只有一对将
//去将 后判断方案
List<Integer> tmphc = new ArrayList<>();
tmphc.addAll(cardInHand);
Util.removeCard(tmphc,jmap.get("jiangcard").get(0),2);
Map<String, Object> tmap = new HashMap<>();
checkNormalHu(tmphc, tmap);
//不去将 判断
Map<String, Object> tmap2 = new HashMap<>();
System.out.println(cardInHand);
checkNormalHu(cardInHand, tmap2);
System.out.println(tmap2);
}else if(jiangnum>1){
}else{
}*/
checkNormalHu(cardInHand, map);
List<Integer> suggested = (List<Integer>) map.get("cardResiue");
//返回要打的牌,打后可以听牌
List<Integer> zuizhongchupai = new ArrayList<>();
//如果打出然后可以听牌,跟递归判断出来的牌一样,优先打出这张
if (checktingpai.size() > 0) {
for (Integer integer : checktingpai) {
for (Integer integer1 : suggested) {
if (integer.equals(integer1)) {
zuizhongchupai.add(integer);
}
}
}
}
//推荐打几个牌都能听牌,需要判断打哪个听的牌更多
if (checktingpai.size() > 1 && zuizhongchupai.size() == 0) {
zuizhongchupai.addAll(checktingpai);
}
//如果判断听牌和递归出来的牌不一样 优先以听牌为准
if (zuizhongchupai.size() == 0) {
zuizhongchupai.addAll(checktingpai);
}
//如果判断最后打出牌还未听牌,就用递归判断的牌
if (zuizhongchupai.size() == 0) {
zuizhongchupai.addAll(suggested);
}
System.out.println("zuizhongchupai" + zuizhongchupai);
if (zuizhongchupai.isEmpty()) {
return new ArrayList<>();
}
// //牌桌上出过的牌 未实现 这里听牌zuizhongchupai 对比 牌桌上出过的牌
// ChangShaSuanFaTest changShaSuanFaTest = new ChangShaSuanFaTest();
// List<Integer> chuguodepai = changShaSuanFaTest.chuguodepai();
// if (chuguodepai.size() > 0 && checktingpai.size() > 0) {
// System.out.println("递归内牌桌上出过的牌 ============ ===============" + chuguodepai);
// Map<Integer, Integer> tinCardCountMap = new HashMap<>();
//
// // 初始化听牌组中所有牌的出现次数为0
// for (Integer card : checktingpai) {
// tinCardCountMap.put(card, 0);
// }
//
// // 统计听牌组中每张牌在牌桌上的实际出现次数
// for (Integer card : checktingpai) {
// if (tinCardCountMap.containsKey(card)) {
// tinCardCountMap.put(card, tinCardCountMap.get(card) + 1);
// }
// }
//
// // 找到最大的出现次数
// Integer maxCount = tinCardCountMap.values().stream()
// .max(Integer::compareTo)
// .orElse(0);
//
// // 判断所有听牌是否都大于等于3次
// boolean allGreaterOrEqualThree = !tinCardCountMap.isEmpty() &&
// tinCardCountMap.values().stream().allMatch(count -> count >= 3);
//
// if (allGreaterOrEqualThree) {
// List<Integer> result = new ArrayList<>();
// // 如果所有听牌都出现大于等于3次则随机出一张听牌
// Integer randomCard = checktingpai.get(new Random().nextInt(checktingpai.size()));
// System.out.println("递归内 -----所有听牌出现次数都>=3随机出牌: " + randomCard);
// result.add(randomCard);
// return result;
// }
//
// if (maxCount > 0 && maxCount < 3) {
// // 出现次数大于0但小于3也直接出次数最多的牌
// List<Integer> maxCards = tinCardCountMap.entrySet().stream()
// .filter(entry -> entry.getValue().equals(maxCount))
// .map(Map.Entry::getKey)
// .collect(Collectors.toList());
//
// if (!maxCards.isEmpty()) {
// List<Integer> result = new ArrayList<>();
// Integer maxCard = Collections.max(maxCards);
// result.add(maxCard);
// return result;
// }
//
// }
// }
return zuizhongchupai;
}
// 计算听牌数量(简化版,只计算数量不具体分析是哪些牌)
private static int countTingCards(List<Integer> hand) {
int tingCount = 0;
// 只尝试添加万、筒、条牌1-9
for (int type = 1; type <= 3; type++) {
for (int value = 1; value <= 9; value++) {
int testCard = type * 100 + value;
// 模拟添加这张牌
List<Integer> testHand = new ArrayList<>(hand);
testHand.add(testCard);
Collections.sort(testHand);
// 检查是否能胡牌
Map<String, Object> testMap = new HashMap<>();
if (checkNormalHu(testHand, testMap) == 0) {
tingCount++;
}
}
}
// 风牌和箭牌(可能性较小,但也要考虑)
for (int type = 4; type <= 5; type++) {
int max = (type == 4) ? 4 : 3;
for (int value = 1; value <= max; value++) {
int testCard = type * 100 + value;
List<Integer> testHand = new ArrayList<>(hand);
testHand.add(testCard);
Collections.sort(testHand);
Map<String, Object> testMap = new HashMap<>();
if (checkNormalHu(testHand, testMap) == 0) {
tingCount++;
}
}
}
return tingCount;
}
// 或者使用更简洁的lambda写法确保能进入
// 或者更灵活的版本,可以指定优先级
public static Integer selectBestCardByPriority(List<Integer> cards) {
if (cards == null || cards.isEmpty()) {
return null;
}
Integer bestCard = null;
int bestPriority = Integer.MAX_VALUE;
for (Integer card : cards) {
int value = card % 100;
int priority = getCardPriority(value);
if (priority < bestPriority) {
bestPriority = priority;
bestCard = card;
} else if (priority == bestPriority) {
// 优先级相同,比较牌值(小的优先)
if (bestCard == null || value < (bestCard % 100)) {
bestCard = card;
}
}
}
return bestCard;
}
// 牌的优先级(数字越小优先级越高)
private static int getCardPriority(int value) {
if (value == 9) return 1;
if (value == 1) return 2;
if (value == 2 || value == 8) return 3; // 靠近边张
if (value >= 3 && value <= 7) return 4; // 中间牌
return 5; // 其他
}
public static Integer selectBestCardRemove258(List<Integer> cards) {
if (cards == null || cards.isEmpty()) {
return null;
}
// 如果只有一张牌,直接返回
if (cards.size() == 1) {
return cards.get(0);
}
// 创建非258牌的列表
List<Integer> non258Cards = new ArrayList<>();
List<Integer> is258Cards = new ArrayList<>();
// 分类258牌和非258牌
for (Integer card : cards) {
if (is258Jiang(card)) {
is258Cards.add(card);
} else {
non258Cards.add(card);
}
}
System.out.println("258将牌: " + is258Cards);
System.out.println("非258牌: " + non258Cards);
// 优先从非258牌中选
if (!non258Cards.isEmpty()) {
return selectBestSingleCard(non258Cards);
}
// 如果没有非258牌从258牌中选
if (!is258Cards.isEmpty()) {
return selectBestSingleCard(is258Cards);
}
return null;
}
// 检测是否满足258做将的条件
private static boolean is258Jiang(int card) {
// 提取牌的类型和值
int type = card / 100; // 百位数1-万2-筒3-条4-风5-箭
int value = card % 100; // 个位数:牌面值
// 只有万、筒、条有258做将的限制
if (type >= 1 && type <= 3) {
// 258做将二万(102)、五万(105)、八万(108)
// 二筒(202)、五筒(205)、八筒(208)
// 二条(302)、五条(305)、八条(308)
return value == 2 || value == 5 || value == 8;
}
return false;
}
// 从候选牌中选出最优的一张牌优先边张1或9
public static Integer selectBestSingleCard(List<Integer> cards) {
if (cards == null || cards.isEmpty()) {
return null;
}
// 优先找边张1或9
for (Integer card : cards) {
int value = card % 100;
if (value == 1 || value == 9) {
return card;
}
}
// 没有边张找靠近边张的牌2或8- 注意这里2是258将牌但我们已经去除了258
for (Integer card : cards) {
int value = card % 100;
if (value == 2 || value == 8) {
return card;
}
}
// 都没有,返回第一张
return cards.get(0);
}
// 主方法
public static void main(String[] args) {
// 测试用例没有258将牌的情况
List<Integer> test1 = new ArrayList<Integer>();
// test1 = Arrays.asList(208, 204, 203, 202, 107, 106, 105, 103, 102, 101, 103);
// test1.add(208);
// test1.add(204);
// test1.add(203);
// test1.add(202);
// test1.add(107);
// test1.add(106);
// test1.add(105);
// test1.add(103);
// test1.add(102);
// test1.add(101);
// test1.add(101);
// test1.add(208);
// test1.add(207);
// test1.add(205);
// test1.add(204);
// test1.add(203);
// test1.add(202);
// test1.add(201);
// test1.add(109);
// test1.add(108);
// test1.add(106);
// test1.add(205);
// test1.add(205);
// test1.add(203);
// test1.add(202);
// test1.add(201);
// test1.add(204);
// test1.add(107);
// test1.add(106);
// test1.add(105);
// test1.add(201);
// test1.add(107);
// test1.add(107);
// test1.add(104);
// test1.add(101);
// test1.add(102);
// test1.add(103);
// test1.add(104);
// test1.add(109);
// test1.add(201);
// test1.add(204);
// test1.add(204);
// test1.add(206);
// test1.add(207);
// test1.add(207);
// test1.add(209);
// test1.add(209);
// test1.add(209);
//
//
test1.add(206);
test1.add(205);
test1.add(204);
test1.add(203);
test1.add(202);
test1.add(209);
test1.add(201);
test1.add(108);
test1.add(107);
test1.add(106);
test1.add(104);
test1.add(103);
test1.add(102);
test1.add(101);
// test1.add(103);
// test1.add(102);
System.out.println("手牌:" + test1);
List<Integer> bestDiscard = analyzeBestDiscard(test1);
// Integer integer = selectBestCardByPriority(bestDiscard);
Integer integer1 = selectBestCardRemove258(bestDiscard);
System.out.println("ww" +integer1);
// if (!bestDiscard.isEmpty()) {
// System.out.println("建议出牌:" + bestDiscard.get(0));
// } else {
// System.out.println("没有出牌建议");
// }
}
}