鬥地主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;
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章