斗地主Console显示版

斗地主:字符串+正则的简单实现


缘由:听同学说学了集合后,就可以基本实现斗地主功能,一开始听到觉得不可能吧,后面就认真构想了一下,觉得如果加上正则匹配扑克牌出牌规则,实现斗地主是可以的,只不过没有图形界面,只能在终端一个人玩,觉得写出来了也挺有意思的,于是我就开始斗地主的编写(ps:写了很久)。
自己测试很多次,没有发现什么bug。
如果有什么逻辑没考虑到,或者优化的地方希望评论告诉我,共同学习,共同进步(^_^)。

整体思路概述:

    1:生成扑克牌,给玩家随机发牌
        1.1:利用扑克牌字符串与索引一一对应关系,通过随机给玩家发索引,然后根据玩家的索引获取牌,来达到给玩家随机发牌的效果。
    2:轮流出牌
        2.1利用循环实现玩家轮流出牌,对每个玩家的出牌进行校验:
                a是否为扑克牌校验,
                b是否是来自玩家自己手中的牌,
                c正则匹配是否符合出牌规则,记录对应牌数据:牌的类型,和牌的大小
        2.2下一个玩家出牌(玩家可选择出牌或跳过),校验成功后,对牌类型和大小比较,满足出牌大小条件,则继续下一个玩家。(注:玩家选择跳过,跳过两次表示其它玩家要不起,那么则需要重置牌记录的数据)
    说明:
        1:3带1不能带一对牌,下面的正则匹配时:一对牌当作2个单牌匹配
        2:没把连炸弹带牌(333344445678)考虑进去
        3:可以把正则匹配方法单独提取出来测试牌    

下面是代码:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

public class FightLandlord {
    private static Scanner sc;
    private static int skip = 0;// 记录玩家跳过的次数
    private static String name = "";// 玩家名字

    public static void main(String[] args) {
        playPoker();
    }

    // 修改:遍历拼接时直接添加到Map集合中
    public static void playPoker() {
        ArrayList<String> p1 = new ArrayList<String>();// 玩家1集合
        ArrayList<String> p2 = new ArrayList<String>();// 玩家2集合
        ArrayList<String> p3 = new ArrayList<String>();// 玩家3集合

        // -----------方法1: 根据字符串索引给玩家发牌-----------
        indexStringPoker(p1, p2, p3);
        String beforeW = "beforeW";// 初始玩家先出牌字符串
        String afterW = "afterW";// 初始玩家:后出牌字符串
        System.out.println("出牌简化:'0'代表 牌'10','小'='小☺',大='大☻',e代表跳过,忽略了大小写.(注:输入其它非扑克牌字符也是默认跳过:1,你...)");
        while (true) {
            // -----------方法2: 循环判断玩家出牌是否合理-----------
            name = "地主 p1_";// 玩家名提示
            beforeW = loopSyso(p1, beforeW, afterW, name);// 4
            name = "农民 p2_";
            beforeW = loopSyso(p2, beforeW, afterW, name);
            name = "农民 p3_";
            beforeW = loopSyso(p3, beforeW, afterW, name);
        }
    }

    // -------------------------------方法1-1:根据字符串索引给玩家发牌-------------------------------
    public static void indexStringPoker(ArrayList<String> p1, ArrayList<String> p2, ArrayList<String> p3) {
        // 为了方便排序,临时替换: a替换0 b=j c=q d=k e=A f=2 小=g 大=h;
        String cards = "3♣3♠3♥3♦4♣4♠4♥4♦5♣5♠5♥5♦6♣6♠6♥6♦7♣7♠7♥7♦8♣8♠8♥8♦9♣9♠9♥9♦a♣a♠a♥a♦b♣b♠b♥b♦c♣c♠c♥c♦d♣d♠d♥d♦e♣e♠e♥e♦f♣f♠f♥f♦g☺h☻";
        ArrayList<Integer> key = new ArrayList<Integer>();// key集合=对应牌索引
        for (int i = 0; i < 54; i++) {// 给集合添加内容:(0-53)
            key.add(i);
        }
        Collections.shuffle(key);// 打乱键(索引)=打乱牌
        // 给玩家添加牌
        for (int i = 0; i < 54; i++) {
            int index = key.get(i);
            if (i < 20)
                p1.add(cards.substring(2 * index, 2 * (index + 1)));
            if (20 <= i && i < 37)
                p2.add(cards.substring(2 * index, 2 * (index + 1)));
            if (i >= 37)
                p3.add(cards.substring(2 * index, 2 * (index + 1)));
        }
        // -----------方法1-1-1: 替换回来-->a替换0 b=j c=q d=k e=A f=2-----------
        replaceSort(p1);
        replaceSort(p2);
        replaceSort(p3);
        System.out.println("p1: " + p1 + p1.size());
        System.out.println("p2: " + p2 + p2.size());
        System.out.println("p3: " + p3 + p3.size());
    }

    // ----------------------方法1-1-1:建字符串替换回来,并转换为数组----------------------
    public static void replaceSort(ArrayList<String> p) {
        Collections.sort(p);// 对玩家牌排序
        String ps = p.toString();// 转换为字符串
        ps = ps.replaceAll("a", "0");
        ps = ps.replaceAll("b", "J");
        ps = ps.replaceAll("c", "Q");
        ps = ps.replaceAll("d", "K");
        ps = ps.replaceAll("e", "A");
        ps = ps.replaceAll("f", "2");
        // 必须完全排序,否则j索引跳过顺序混乱的牌.---->大小-->遍历时,j直接累加到了"大",最后的索引.当再依次遍历到小的时候.在大之后索引查找,找不到了
        ps = ps.replaceAll("g", "小");
        ps = ps.replaceAll("h", "大");
        ps = ps.replaceAll("[\\[\\]]", "");// 去除前后大括号
        String[] split = ps.split(",");// 转化为数组
        for (int i = 0; i < split.length; i++) {
            p.set(i, split[i]);
        }
    }

    // ------------------------方法2:循环出牌-------------------------------
    public static String loopSyso(ArrayList<String> p, String beforeW, String afterW, String name) {
        sc = new Scanner(System.in);
        // 接收玩家牌,判断
        w: while (true) {
            System.out.print(name + p + "\n\t\t" + name + "出牌:");
            afterW = sc.next();// 接收玩家的出牌

            // -----------方法2-1: 对玩家输入的牌进行排序-----------
            afterW = diySortString(afterW);
            if (afterW.contains("E")) {
                skip++;
                if (skip == 3) {
                    System.out.println("跳过一轮");
                    skip = 0;
                }
                System.out.print("_______________________________" + name + "跳过:" + skip + "次______________________\n");
                return beforeW;// 返回上家的牌
            }

            // 遍历玩家输入的字符串,记录对应牌索引()
            int[] pcopyI = new int[afterW.length()];// 默认为0
            int j = -1;// 记录上次遍历到的索引位置
            f: for (int i = 0; i < afterW.length(); i++) {// 遍历玩家字符串
                String s1 = afterW.substring(i, i + 1);// 拿到玩家字符串的每个字符
                // 不回头遍历:必须有序
                for (j++; j < p.size(); j++) {// 遍历玩家p牌对应的字符数组
                    String s = p.get(j);
                    if (s.contains(s1)) {// 对比是否有相同的牌: "3♣"包含3,匹配成功
                        pcopyI[i] = j;// 记录牌中被替换对应的索引
                        continue f;// 跳转到外循环,匹配下一个字符
                    }
                }
                System.out.println("你输入的牌有不存在的,请重新输入");
                continue w;// 有不匹配字符,则跳转到开始:提示玩家重新输入
            }

            // ----------方法2-2:校验比较牌大小------
            int[] w1 = regexMatches(beforeW);// 获取玩家"牌数据"(出牌类型,及对应出牌大小)
            // 都跳过,玩家可自定义出牌---位置必须放在w1=后用于重置array"牌数据"
            if (skip == 2) {// 跳过2次,说明其它玩家都不要,那么"牌数据"还原为初始值
                w1[0] = -1;
                w1[1] = 0;
            }
            // 当前玩家出牌
            int[] w1c = regexMatches(afterW);
            if (w1c[0] == -2) {// 牌不符合规则的终止条件:终止
                continue;// -->玩家重新输入
            }
            System.out.println("前类型=" + w1[1] + ",后类型=" + w1c[1] + ", 前:=" + w1[0] + ",后=" + w1c[0]);

            // 比较类型
            if (w1[1] == w1c[1] | w1c[1] == 10000 | w1[1] == 0) {
                if (w1[0] >= w1c[0]) {// 比较牌大小
                    w1c[0] = w1[0];// 还原上家牌索引
                    w1c[1] = w1[1];// 还原上家牌类型
                    System.out.println("要不起,请重新选择牌");
                    continue;
                }
            } else {// 类型不满足
                w1c[1] = w1[1];
                w1c[0] = w1[0];
                System.out.println("类型不匹配,请重新出牌");
                continue;
            }
            // 当玩家不是连续输入重置跳过次数=位置需在skip==2后//中间玩家输错,不至于更改
            if (!afterW.contains("E")) {
                skip = 0;
            }
            // 显示出牌
            System.out.print("________________________________" + name + afterW + "________________________\n");
            // 移除牌
            for (int i = 0; i < pcopyI.length; i++) {
                p.set(pcopyI[i], "");
            }
            break w;// 出牌成功跳出循环
        }
        // ---------------------------------判断玩家是否出完牌--------------------------
        for (int i = 0; i < p.size(); i++) {
            if (!p.get(i).equals("")) {
                break;
            }
            if (i == (p.size() - 1)) {
                System.out.println("________________________________" + name + " 先出完牌,game over");
                System.exit(3000);
                // return "over";//返回该上一层,判断是否退出
            }
        }
        beforeW = afterW;// 内存地址
        return beforeW;// 默认值
    }

    // -----------方法2-1:对用户输入的字符串进行排序-----------
    public static String diySortString(String str) {
        str = str.toUpperCase();// 忽略大小写
        String sortS = "34567890JQKA2小☺大☻";// 指定排序规则字符串
        if (!str.matches("[" + sortS + "]+")) {// 匹配是否非扑克牌字符,是跳过
            return "E";
        }
        ArrayList<Integer> strA = new ArrayList<Integer>();
        for (int i = 0; i < str.length(); i++) {
            strA.add(sortS.indexOf(str.charAt(i)));// 匹配待排序字符串中的每个字符,对应在规则字符串中的索引,并添加
        }
        Collections.sort(strA);// 将字符的索引从小到大排序
        String sortWp = "";
        for (int i = 0; i < strA.size(); i++) {
            sortWp = sortWp + sortS.charAt(strA.get(i));// 通过索引获取对应字符
        }
        return sortWp;
    }

    // ------------------------方法2-2: 正则表达式-------------------

    public static int[] regexMatches(String wp) {
        int[] array = { -1, 0 };// 初始"牌数据"
        String sCards = "34567890JQKA2小大";// 单牌=规则大小牌
        String linkCards1 = "34567890JQKA";// 单连牌
        String linkCards2 = "3344556677889900JJQQKKAA";// 对连牌
        String linkCards3 = "333444555666777888999000JJJQQQKKKAAA";// 3连牌
        String linkCards4 = "33334444555566667777888899990000JJJJQQQQKKKKAAAA";// 4连牌

        // 匹配出牌属于哪一类型
        int index = -1;
        if (wp.length() == 1) {
            index = sCards.indexOf(wp);
            array[0] = index;
            array[1] = 1;
            return array;// 单牌
        } else if (linkCards1.contains(wp) && wp.length() > 4) {
            index = linkCards1.indexOf(wp);
            array[0] = index;
            array[1] = 11;
            return array;// 单连牌
        } else if (wp.matches("(.)\\1")) {
            index = sCards.indexOf(wp.charAt(0));
            array[0] = index;
            array[1] = 2;
            return array;// 对牌
        } else if (linkCards2.contains(wp) && wp.length() > 5 && linkCards2.indexOf(wp) % 2 == 0) {
            index = linkCards2.indexOf(wp);
            array[0] = index;
            array[1] = 21;
            return array;// 对连牌
        } else if (wp.matches("(.)\\1\\1")) {
            index = linkCards3.indexOf(wp);
            array[0] = index;
            array[1] = 3;
            return array;// 三牌
        } else if (!wp.matches("(.)\\1{3}") && (wp.matches(".(.)\\1\\1")) | wp.matches("(.)\\1\\1.")) {// 防止误匹配4个相同的
            index = sCards.indexOf(wp.charAt(2));// 获取中间字符值,对应规则字符串中的索引,作为比较
            array[0] = index;
            array[1] = 31;
            return array;// 三带一
        } else if (linkCards3.contains(wp) && linkCards3.indexOf(wp) % 3 == 0 && wp.length() > 5) {
            index = linkCards3.indexOf(wp);
            array[0] = index;
            array[1] = 33;
            return array;// 三连牌
        } else if (wp.length() > 7 && linkCards3.contains(wp.replaceAll("[" + wp.replaceAll("(.)\\1\\1", "") + "]", ""))
                && wp.replaceAll("[" + wp.replaceAll("(.)\\1\\1", "") + "]", "").length() / 3 == wp
                        .replaceAll("(.)\\1\\1", "").length()) {
            // 长度8 12 // 满足连续关系// 三带1模式:匹配3个相同的数量/3等于额外数 ==4的倍数
            index = linkCards3.indexOf(wp.replaceAll("[" + wp.replaceAll("(.)\\1\\1", "") + "]", ""));
            array[0] = index;
            array[1] = 3311;
            return array;// 飞机
        } else if (linkCards4.contains(wp) && linkCards3.indexOf(wp) % 4 == 0) {
            array[0] = linkCards4.indexOf(wp);
            array[1] = 44;
            return array;// 四连牌
        } else if (wp.matches("(.)\\1{3}.{2}") | wp.matches("..(.)\\1{3}")) {
            index = sCards.indexOf(wp.charAt(2));// 44|5555|66
            array[0] = index;
            array[1] = 42;
            return array;// 四带二
        } else if (wp.matches("(.)\\1{3}") | wp.equals("小大")) {
            index = sCards.indexOf(wp.charAt(0));
            array[0] = index;
            array[1] = 10000;
            return array;// 炸弹
        } else {
            if (wp.matches("beforeW")) {// 初始值,则返回默认类型
                return array;
            } else {
                System.out.println("你输入的牌不符合规则");
                array[0] = -2;// 用于终止继续执行条件
                return array;
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章