1. 程式人生 > >java:Map集合模擬鬥地主,多執行緒模擬搶地主 例項

java:Map集合模擬鬥地主,多執行緒模擬搶地主 例項

 原始碼如下:

package selfpractice.day4;

import java.util.*;
//多執行緒模擬搶地,重點程式碼位於loot()方法內
public class Practice_Poker {
    public static void main(String[] args) {//入口

        //獲得一幅撲克牌存放於HashMap中
        HashMap<Integer, String> myPoker = newPoker();

        //洗牌後將撲克牌鍵值存放於連結串列中
        LinkedList<Integer> myPokerPoint = shuffle();

        //存放玩家撲克牌鍵值的連結串列,每個連結串列代表一個玩家
        LinkedList<Integer> player1 = new LinkedList<>();
        LinkedList<Integer> player2 = new LinkedList<>();
        LinkedList<Integer> player3 = new LinkedList<>();

        //起牌按現實世界獲得17張撲克牌,剩餘3張即底牌
        getPoker(myPokerPoint,player1,player2,player3);


        LinkedList<Integer> diPai = new LinkedList<>(myPokerPoint);//lambda不讓傳入可變引數myPoker 展示底牌放到方法執行後
        loot(myPokerPoint,player1,player2,player3);//搶地主
        //展示地底牌
        for (Integer integer : diPai) {
            System.out.print(myPoker.get(integer)+" ");
        }
        System.out.println();//換個行 對齊

        try {//等待控制檯輸出完畢前面的內容,否則結果可能格式混亂
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //三位玩家理牌
        System.out.println("三位玩家理牌...");
        sort(player1);
        sort(player2);
        sort(player3);

        //展示牌面,最後揭曉底牌
        System.out.print("玩家一:");
        showPoker(player1,myPoker);
        System.out.print("玩家二:");
        showPoker(player2,myPoker);
        System.out.print("玩家三:");
        showPoker(player3,myPoker);
    }

    //獲得撲克牌方法:來一幅撲克牌,返回一個包含54張牌的HashMap陣列,鍵值升序0~53,對應鬥地主規則牌面值大小
    private static HashMap<Integer,String> newPoker(){
        HashMap<Integer,String> hashMap=new HashMap<>();//存放撲克牌的HashMap
        String[] colors={"♥","♠","♦","♣"};//花色陣列
        String[] nums={"3","4","5","6","7","8","9","10","J","Q","K","A","2"};//牌面陣列
        int term=0;//鍵值
        //遍歷加入前52張牌
        for (String num:nums) {
            for (String color:colors) {
                hashMap.put(term++,color+num);
            }
        }
        //新增大小王
        hashMap.put(term++,"小王");
        hashMap.put(term,"大王");

        System.out.println("得到一副牌");
        return hashMap;
    }


    //洗牌方法,實際洗的是LinkedList陣列
    private static LinkedList<Integer> shuffle(){
        //獲得所有牌的鍵值存入連結串列
        LinkedList<Integer> linkedList=new LinkedList<>();
        for (int i = 0; i < 54; i++) {
            linkedList.add(i);
        }
        Collections.shuffle(linkedList);//洗牌
        System.out.println("洗牌...");
        return linkedList;//返回洗牌結果
    }

    //發牌方法:每起牌一次,撲克牌少一張,真實模擬,剩餘三張為底牌(引數一次為洗好的底牌和三個連結串列玩家)
    private static void getPoker( LinkedList<Integer> myPokerPoint,LinkedList<Integer> player1, LinkedList<Integer> player2,LinkedList<Integer> player3){
        LinkedList<LinkedList<Integer>> playerList=new LinkedList<>();
        //玩家放入迴圈起牌佇列
        playerList.add(player1);
        playerList.add(player2);
        playerList.add(player3);

        //迴圈依次起牌,各17張
        for (int i = 0; i < 51; i++) {
            playerList.getFirst().add(myPokerPoint.removeFirst());
            playerList.addLast(playerList.removeFirst());//迴圈起牌佇列滾動一次,即第一個元素放到最後
        }
        System.out.println("發牌...");
    }

    //理牌方法:用Collections工具的快速排序sort方法
    private static void sort(LinkedList<Integer> linkedList){
        Collections.sort(linkedList);
    }

    //搶地主:利用多執行緒模擬搶地主,傳入
    private static void loot(LinkedList<Integer> myPokerPoint,LinkedList<Integer> player1, LinkedList<Integer> player2,LinkedList<Integer> player3) {
        Object lock1=new Object();//物件鎖1
        //先吹口哨,等待0.5秒,三位玩家執行緒依次就緒
        synchronized (lock1){System.out.println("多執行緒模擬搶地主...\n裁判吹口哨:");
            //等待0.5秒
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("口哨聲...");
            lock1.notifyAll();//釋放物件鎖1開始搶地主
        }
        new Thread(()->{//載入執行緒後等待物件鎖1倍裁判程式釋放,搶到物件鎖開始執行同步程式碼
            synchronized (lock1){
                //若底牌數大於0則拿過來加入自己的牌,並表示搶到地主並展示一下
                //若沒有底牌就算了
                if(myPokerPoint.size()>0) {
                    player1.addAll(myPokerPoint);
                    System.out.println("玩家一搶到地主");
                    System.out.print("展示底牌:");
                    myPokerPoint.clear();
                }
            }
        },"玩家一執行緒").start();

        new Thread(()->{//載入執行緒後等待物件鎖1倍裁判程式釋放
            synchronized (lock1){
                //若底牌數大於0則拿過來加入自己的牌,並表示搶到地主並展示一下
                //若沒有底牌就算了
                if(myPokerPoint.size()>0) {
                    player2.addAll(myPokerPoint);
                    System.out.println("玩家二搶到地主");
                    System.out.print("展示底牌:");
                    myPokerPoint.clear();
                }
            }
        },"玩家二執行緒").start();

        new Thread(()->{//載入執行緒後等待物件鎖1倍裁判程式釋放
            synchronized (lock1){
                //若底牌數大於0則拿過來加入自己的牌,並表示搶到地主並展示一下
                //若沒有底牌就算了
                if(myPokerPoint.size()>0) {
                    player3.addAll(myPokerPoint);
                    System.out.println("玩家三搶到地主");
                    System.out.print("展示底牌:");
                    myPokerPoint.clear();
                }
            }
        },"玩家三執行緒").start(
        );
    }

    //亮牌方法:自動判斷傳入的是玩家還是底牌
    private static void showPoker(LinkedList<Integer> linkedList, HashMap<Integer, String> hashMap){
        if(linkedList.size()==20) System.out.print("(地主)");
        for (int i = 0; i <20; i++) {
            if(linkedList.size()==20){//若是地主
                String str=hashMap.get(linkedList.get(i));
                System.out.print(str+" ");
            }else{//否則是農民
                String str=hashMap.get(linkedList.get(i));
                System.out.print(str+" ");
                if(i==16) break;
            }
        }
        System.out.println();//換行
    }

}

 連續執行三次結果如下:

    第一次:

得到一副牌
洗牌...
發牌...
多執行緒模擬搶地主...
裁判吹口哨:
口哨聲...
玩家三搶到地主
展示底牌:♦10 ♠9 ♠6 
三位玩家理牌...
玩家一:♥5 ♠5 ♣5 ♦6 ♠7 ♦7 ♠8 ♥9 ♦9 ♣9 ♥10 ♠10 ♦J ♣Q ♣K ♥2 小王 
玩家二:♣3 ♥4 ♣4 ♦5 ♥6 ♣6 ♥7 ♣7 ♣8 ♣10 ♥J ♣J ♠K ♦K ♣A ♦2 大王 
玩家三:(地主)♥3 ♠3 ♦3 ♠4 ♦4 ♠6 ♥8 ♦8 ♠9 ♦10 ♠J ♥Q ♠Q ♦Q ♥K ♥A ♠A ♦A ♠2 ♣2 

    第二次:

得到一副牌
洗牌...
發牌...
多執行緒模擬搶地主...
裁判吹口哨:
口哨聲...
玩家一搶到地主
展示底牌:大王 ♦4 ♦6 
三位玩家理牌...
玩家一:(地主)♥3 ♦4 ♣4 ♠6 ♦6 ♠7 ♦7 ♦8 ♣8 ♥9 ♦9 ♣10 ♦J ♣J ♠A ♣A ♥2 ♠2 小王 大王 
玩家二:♣3 ♥5 ♠5 ♦5 ♣5 ♥7 ♣7 ♥8 ♣9 ♦10 ♥Q ♦Q ♠K ♦K ♣K ♥A ♣2 
玩家三:♠3 ♦3 ♥4 ♠4 ♥6 ♣6 ♠8 ♠9 ♥10 ♠10 ♥J ♠J ♠Q ♣Q ♥K ♦A ♦2

    第三次:

得到一副牌
洗牌...
發牌...
多執行緒模擬搶地主...
裁判吹口哨:
口哨聲...
玩家二搶到地主
展示底牌:♥9 ♠8 ♦4 
三位玩家理牌...
玩家一:♥3 ♣4 ♥5 ♠5 ♥6 ♣6 ♦8 ♣8 ♣9 ♥10 ♦10 ♣10 ♦J ♣Q ♥K ♥A ♠A 
玩家二:(地主)♠3 ♥4 ♦4 ♦5 ♣5 ♦6 ♥7 ♠7 ♠8 ♥9 ♠9 ♥J ♠J ♣J ♠Q ♦Q ♠2 ♦2 ♣2 大王 
玩家三:♦3 ♣3 ♠4 ♠6 ♦7 ♣7 ♥8 ♦9 ♠10 ♥Q ♠K ♦K ♣K ♦A ♣A ♥2 小王

    機緣巧合,各搶到一次地主!!!!大家可以自己複製程式碼執行.

    思維過程已經用在程式碼中註釋出,可以複製程式碼後利用除錯模式跟隨我的思路一步步執行程式碼!

程式設計想:

    1/ 利用多執行緒模擬搶地主,加入"裁判程式",率先獲得同步物件鎖.  以保障三個玩家執行緒全部建立完畢後,再同時爭奪物件鎖,執行鎖內程式碼,可以保證競爭絕對公平;

    2/因為java控制檯字元輸出有延遲,故中間搶完地主後置了一段等待後臺輸出的時間;

    3/根據本程式碼思維,利用HashMap的雙列集合特點,可以完成任何像撲克牌這種無法用自然排序集合的自定義排序;