人机斗地主小游戏(二)

149 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第二天,点击查看活动详情


上一章写的是洗牌发牌,接下来就要玩家出牌,系统判断玩家出的牌是否符合规则,然后按照玩家出的牌模拟人机出牌。

循环所有的牌,玩家出牌后在list中删除所出的牌,知道出完所有的牌,结束游戏

List<String> boards = new ArrayList();
int m = 0;
while (allPok.size() > 0) {
    if (boards != null && boards.size() > 0) {
        //如果返回end,则结束游戏
        if (boards.get(0).equals("end")) {
            break;
        }
    }
    for (Integer key : peoperMap.keySet()) {//keySet获取map集合key的集合  然后在遍历key即可
        if (key == m && m != 0) {
            //如果后面的玩家都要不起,则由当前玩家重新出牌
            boards.clear();
        }
        //地主需要第一个出牌
        if (!(key == randomLand) && m == 0) {
            continue;
        }
        //输出带颜色的提示信息,好分辨是哪个玩家的信息
        printSingleColor("", key + 31, key + 31, "【" + peoperMap.get(key) + "】出牌=====================================");
        try {
            //休眠一段时间防止出牌速度过快
            Thread.sleep(Long.valueOf(RandomStringUtils.randomNumeric(3)));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //当前玩家可以看到自己的牌,不能看到其他人的牌
        if (local.equals(peoperMap.get(key))) {
            System.out.println("【" + peoperMap.get(key) + "】的牌为:" + pokMap.get(peoperMap.get(key)).toString());
        }
        //如果是手动出牌,则列出所有的牌
        if (!self && local.equals(peoperMap.get(key))) {
//                    boards.clear();
            String board = "";
            System.out.print("请输入你要出的牌:");
            board = scanner.nextLine();
            //调用方法判断是否出牌有误
            while (!checkBoard(board, pokMap.get(peoperMap.get(key)), boards, valueMap)) {
                System.out.print("出牌有误,请输入你要出的牌:");
                board = scanner.nextLine();
            }
            if (StringUtils.isNotEmpty(board)) {
                char[] chars = board.toCharArray();
                boards = new ArrayList() {{
                    for (char c1 : chars) {
                        String s = String.valueOf(c1);
                        if (s.contains("大") || s.contains("小")) {
                            s = s.contains("大") ? "大王" : "小王";
                        } else if (s.contains("王")) {
                            continue;
                        } else if (s.contains("0")) {
                            s = "10";
                        } else if (s.contains("1")) {
                            continue;
                        }
                        final String p = s;

                        //根据出牌的数字匹配对应的牌号
                        //要存储不同符号的牌号
                        String v = pokMap.get(peoperMap.get(key)).stream().filter(t ->
                                getNum(t.toString()).equals(StringUtils.upperCase(p)) && !contains(t)).findFirst().get().toString();
                        add(v);
                    }
                }};

            } else {
                boards.clear();
            }
        }
        //模拟出牌操作,判断出牌是否有误
        List retBoards = playHandPlayer1(peoperMap.get(key), pokMap, valueMap, allPok, boards, local, self);
        List b = new ArrayList();
        if (retBoards != null) {
            b.addAll(retBoards);
        }
        //如果下家都要不起,则由该玩家重新出牌
        if (retBoards != null && retBoards.size() > 0) {
            //记录最后出牌的玩家
            m = key;
            boards.clear();
            boards.addAll(b);
            if (b.get(0).equals("end")) {
                break;
            }
        }

    }

}
  1. 该方法为checkBoard方法,根据牌的序号数判断该玩家的牌出的是否符合规则

循环玩家所出的牌,判断是否在玩家的牌池(之前随机分发给玩家的牌)中,放入list集合中

//如果玩家输入的出牌为空则表示要不起
if (StringUtils.isEmpty(board)) {
    if (retBoards == null || retBoards.size() == 0) {
        return false;
    }
    return true;
}
Map<String, String> map = new HashMap();
char[] chars = board.toCharArray();
List<String> charBoard = new ArrayList() {{
    for (char c1 : chars) {
        String s = String.valueOf(c1);
        final String p = s;
        //判断玩家是否有这张牌
        Object v = pokList.stream().filter(t ->
                getNum(t).equals(StringUtils.upperCase(p)) && !contains(t)).findFirst();
        if (v == null) {
            add("false");
            break;
        }
        //大王小王以及10等字符可通过是否包含方式进行判断
        if (s.contains("大") || s.contains("小")) {
            add(s.contains("大") ? "大王" : "小王");
        } else if (s.contains("0")) {
            add("10");
        } else if (s.contains("王")) {
            continue;
        } else if (s.contains("1")) {
            continue;
        } else {
            add(s.toUpperCase());
        }
    }
}};
if (charBoard.contains("false")) {
    return false;
}

charBoard为玩家出的牌,打印效果如下

image.png

  1. 初始化牌,将相同的牌提取出来,为自动出牌做准备
   Map<Integer, List<String>> initMap = initBoard(charBoard, valueMap);

获取玩家卡牌中重复的牌,如对子,三张,炸弹等,其中王炸也在炸弹中,将之放入各个对应的List集合中

public static Map<Integer, List<String>> initBoard(List<String> poker, Map<String, Integer> valueMap) {
    //判断数组中重复的数字与次数
    Map<Integer, Long> repetMap = poker.stream()
            .filter(p -> valueMap.get(p) != null).collect(
                    Collectors.groupingBy(p -> valueMap.get(p), Collectors.counting()));

    Map<String, Long> repetNum = new HashMap<>();
    repetMap.keySet().forEach(m -> {
        poker.stream().forEach(p -> {
            if (valueMap.get(p) == m) {
                repetNum.put(p, repetMap.get(m));
            }
        });
    });


    List<String> oneList = new ArrayList<>();
    List<String> twoList = new ArrayList<>();
    List<String> threeList = new ArrayList<>();
    List<String> fourList = new ArrayList<>();

    repetNum.forEach((k, v) -> {
        switch (v.intValue()) {
            case 1:
                oneList.add(k);
                break;
            case 2:
                twoList.add(k);
                break;
            case 3:
                threeList.add(k);
                break;
            case 4:
                fourList.add(k);
                break;
        }
    });
    if (oneList.contains("大王") && oneList.contains("小王")) {
        fourList.add("大王");
        fourList.add("小王");
        oneList.remove("大王");
        oneList.remove("小王");

    }
    //两张牌加三张牌组为连对
//        twoList.addAll(threeList);

    Map<Integer, List<String>> listMap = new HashMap<>();
    listMap.put(1, oneList);
    listMap.put(2, twoList);
    listMap.put(3, threeList);
    listMap.put(4, fourList);

    return listMap;
}
  1. 将初始化之后的牌整理归类,判断是否连续
 //判断只能有一个list大小大于0
int m = initMap.keySet().stream().filter(n -> initMap.get(n).size() > 0).toArray().length;
if (m >= 2) {
    return false;
}
//取出不为空list的数值
Integer num = initMap.keySet().stream().filter(n -> initMap.get(n).size() > 0).findFirst().get();
//获取出不为空的list
List<String> list = initMap.get(num);
if (retBoards.size() == 0) {
    //判断是否连续
    switch (num) {
        case 1:
            //如果是单张的牌,则需要判断是否是顺子,最少为五张连续的牌
            if (list.size() == 1) {
                return true;
            }
            if (list.size() >= 5) {
                Map map1 = clocks(list, valueMap, 5);
                if (map1.size() > 0) {
                    return true;
                }
            }
            return false;
        case 2:
            //如果是对子,则判断是否为连对,最少需要三次连续的牌
            if (list.size() == 1) {
                return true;
            }
            if (list.size() >= 3) {
                Map map1 = clocks(list, valueMap, 3);
                if (map1.size() > 0) {
                    return true;
                }
            }
            return false;

        case 3:
           //如果是三张,则判断是否为飞机,最少需要两次连续的牌
            if (list.size() == 1) {
                return true;
            }
            if (list.size() >= 2) {
                Map map1 = clocks(list, valueMap, 2);
                if (map1.size() > 0) {
                    return true;
                }
            }
            return false;
    }

}

判断是否是顺序的牌

/**
 * 获取顺序的牌的第一张与张数
 *
 * @param valueMap
 * @param num      顺子至少需要连续的五张牌 连对则需三对 飞机为2
 * @return
 */
public static Map<String, Integer> clocks(List<String> list, Map<String, Integer> valueMap, int num) {
    //先去重
    ArrayList<String> li = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
            new TreeSet<>(Comparator.comparing(o ->
                    getNum(o)))), ArrayList::new));

    Map<String, Integer> clocksMap = new HashMap();
    Stream.iterate(0, i -> i + 1).limit(li.size()).forEach(index -> {
        int returnIndex = checkClockAfter(li, index, valueMap);
        //数据相减后与传过来的数据减一做对比,如顺子最大的index为5,最小的index为1,相减后为4
        if (returnIndex - index >= num - 1) {
            //key : 连对第一张牌 value : 连对对数
            clocksMap.put(li.get(index), returnIndex - index + 1);
        }
    });
    return clocksMap;
}
  1. 上面的方法是判断这一次出牌是否无误,接下来要判断该出牌是否大于上家出的牌,以及是否与上家出的牌匹配
//同样初始化数据,取出单张对子三张炸弹等牌
Map<Integer, List<String>> rbtInitMap = initBoard(retBoards, valueMap);
//获取出不为空的list
Integer num1 = rbtInitMap.keySet().stream().filter(n -> rbtInitMap.get(n).size() > 0).findFirst().get();
List<String> list1 = rbtInitMap.get(num1);
switch (num) {
    case 1:
        if (num1 != 1) {
            return false;
        }

        if (list.size() == 1) {
            //从valueMap中取出牌对应的数字,判断出牌的数字是否大于上家的出牌
            if (valueMap.get(list.get(0).length() == 1 ? "♥" + list.get(0) : list.get(0)) <= valueMap.get(list1.get(0))) {
                return false;
            }
        }
        if (list.size() >= 5) {
            //判断顺子出牌是否有误且最小的顺子数是否大于上家的出牌
            List retBoard = checkOtherPok(initMap.get(1), null, valueMap, rbtInitMap.get(1).get(0), 1);
            if (retBoard == null) {
                return false;
            }
        }
        break;
    case 2:
        if (num1 != 2) {
            return false;
        }

        if (list.size() == 1) {
            if (valueMap.get("♥" + list.get(0)) <= valueMap.get(list1.get(0))) {
                return false;
            }
        }
        if (list.size() >= 3) {
            List retBoard = checkOtherPok(initMap.get(2), null, valueMap, rbtInitMap.get(2).get(0), 2);
            if (retBoard == null) {
                return false;
            }
        }

        break;
    case 3:
        if (num1 != 3) {
            return false;
        }

        if (list.size() == 1) {
            if (valueMap.get("♥" + list.get(0)) <= valueMap.get(list1.get(0))) {
                return false;
            }
        }
        if (list.size() >= 2) {
            List retBoard = checkOtherPok(initMap.get(3), null, valueMap, rbtInitMap.get(3).get(0), 3);
            if (retBoard == null) {
                return false;
            }
        }
        break;
    case 4:
        if (list.size() == 1) {
            if (list1.size() > 1) {
                return true;
            }
            if (valueMap.get("♥" + list.get(0)) <= valueMap.get(list1.get(0))) {
                return false;
            }
        }
        //王炸则直接返回成功
        if (list.size() == 2) {
            return true;
        } else {
            List retBoard = checkOtherPok(initMap.get(4), null, valueMap, rbtInitMap.get(4).get(0), 4);
            if (retBoard == null) {
                return false;
            }
        }
        break;
}
return true;

判断当前出牌是否大于上家的出牌

 /**
     * 获取单张或者对子或者三带一
     *
     * @param list     匹配对应的list
     * @param ownMap   匹配对应的map
     * @param valueMap
     * @param val      对方出的牌
     * @param bNum
     * @return
     */
    public static List checkOtherPok(List<String> list, Map<String, Integer> ownMap, Map<String, Integer> valueMap, String val, int bNum) {
        List<String> boards = new ArrayList<>();
        list.stream().forEach(li -> {
            if ((StringUtils.isNotEmpty(val) && valueMap.get(li) > valueMap.get(val)) || StringUtils.isEmpty(val)) {
                if (ownMap != null) {
                    int len = ownMap.keySet().stream().filter(n -> getNum(n).equals(getNum(li))).toArray().length;
                    if (len == 0) {
                        //防止需要两张却获取三张的情况
                        //如果是连对则放入两张牌 飞机则为三张
                        int len1 = boards.stream().filter(n -> getNum(n).equals(getNum(li))).toArray().length;
                        if (len1 < bNum && boards.size() < bNum) {
                            boards.add(li);
                        }
                    }
                } else {
                    //防止需要两张却获取三张的情况
                    //如果是连对则放入两张牌 飞机则为三张
                    int len = boards.stream().filter(n -> getNum(n).equals(getNum(li))).toArray().length;
                    if (len < bNum && boards.size() < bNum) {
                        boards.add(li);
                    }
                }
            }
        });
        return boards;
    }

后续明天再写,over