Java實(shí)戰(zhàn)開發(fā)(2)——撲克游戲

本節(jié)內(nèi)容

1.游戲架構(gòu)和功能

2.游戲功能的具體實(shí)現(xiàn)

注:解說以下代碼是按照寫代碼的過程以及思路順序來講解的,所以以文字順序?yàn)橹鳎渲形淖指綆Р糠执a。所有的源代碼在最末尾。

一、游戲架構(gòu)和功能

(一)游戲過程:

1.設(shè)置參與的人數(shù),并給每個人設(shè)置名字(ID號)、序號(某一桌)和初始金幣
2.設(shè)置本局消耗金幣(底注),相當(dāng)于選擇不同等級的房間
3.開始玩游戲。每個人下底注(比如5塊,10塊等),然后給每個人發(fā)張牌(或者多張牌,根據(jù)游戲規(guī)則決定),之后按照順序下注(①->②->③),當(dāng)前下注的人可以選擇{①下注:自由下注,不能比上個玩家少,也不能多于自己的金幣。②跟注:上個玩家下注多少就下注多少。③all-in:自己有多少就下多少。如果他的總金幣比上一個玩家下的注要少,那么就按他的注來,并退還前面玩家的下注差(比如1,2號下注20,三號只有all-in15,那么就會退還1,2號5個金幣)④比牌(前提是玩法只有一張牌):和另外一位玩家比牌,不管輸贏。⑤棄牌:放棄},依次下注,只剩一人時游戲結(jié)束
4.一局游戲結(jié)束,顯示當(dāng)前所有玩家的金幣信息

(二)該游戲里的對象

1.玩家
2.牌
3.游戲中心(在哪里玩,QQ或者微信等)
4.玩家的管理者
5.撲克的管理者
6.規(guī)則:先比大小,再比花色(黑?>紅?梅?>方?)ASCII值分別為(6,3,5,4)所以用ASCII不太方便,所以我們可以自己給黑紅梅方設(shè)值

(三)寫代碼之前需要以下步驟

找對象抽類——>理清類與類之間的關(guān)系——>UML畫圖/架構(gòu)
具體架構(gòu):游戲中心(Game Center)管理Poker Manager和Play Manager類。
  • 由Poker管理一張牌,在Poker類里面有(設(shè)置內(nèi)部類PokerType包含屬性dot、pic Type和tag int(給黑紅梅方設(shè)值,這樣在比較花色的時候直接比較tag值即可),內(nèi)部類之外有pic 和dot 以及compare方法,開牌比較大小)。
  • 由PokerManager來管理一副牌(有一個數(shù)組保存所有的牌,有一些方法來初始化一副牌,以及獲取一張牌等)。游戲中心(Game Center)發(fā)牌的時候就會從PokerManager里面拿牌
  • Player類:包含玩家的編號,姓名,籌碼,是否棄牌(使用接口),玩家的一張牌,玩家的副牌,還有方法(贏錢,輸錢,下注,退還錢如何計(jì)算)
  • PlayManager類:包含初始化所有玩家,獲取一個玩家等方法
  • Game Center類:包含屬性當(dāng)前臺面上的總金額,button(每局的賭注),CurrentPlayer(當(dāng)前玩家),還有其他類的對象。一些方法,初始化數(shù)據(jù),開始游戲,游戲結(jié)束。
  • 定義一個iGame接口來判斷Game Center類里面初始化是否成功,并回調(diào)數(shù)據(jù)給游戲中心

二、撲克游戲具體實(shí)現(xiàn)

1.創(chuàng)建AGameCenter的抽象類和單例,直接在構(gòu)造函數(shù)里面設(shè)計(jì)好執(zhí)行的順序,先初始化數(shù)據(jù),然后是玩家,最后是撲克,后面補(bǔ)充它們的抽象方法和其他方法
public abstract class  AGameCenter implements IGameInitListener{
    private int totalSuccess;
   protected PlayerManager playerManager;
   protected PokerManager pokerManager;
   protected int ante;//臺面的總金額
   protected int totalPlayer;//玩家人數(shù)
   protected int Button;//底注
    protected AGameCenter(){
      //初始化游戲本身的數(shù)據(jù)
        initGame();
        //初始化玩家
        initPlayers();
        //初始化撲克
        initPokers();
    }

    protected abstract void initGame();

    protected abstract void initPokers();

    protected abstract void initPlayers();

    protected abstract void start();

    @Override
    public void onInitSuccess() {
        //對成功的計(jì)數(shù)器+1
    setTotalSuccess(getTotalSuccess()+1);
    }

    @Override
    public void onInitFailure() {

    }
    public void setTotalSuccess(int totalSuccess) {
        this.totalSuccess = totalSuccess;
        if(totalSuccess==3){//因?yàn)橐跏蓟危酝ㄟ^totalSuccess是不是等于三來判斷初始化是否成功
            start();
        }
    }

    public int getTotalSuccess() {
        return totalSuccess;
    }
}
2.創(chuàng)建一個IGameInitListener的接口來判斷初始化是否成功
public interface IGameInitListener {
   void onInitSuccess();
   void onInitFailure();
}
3.創(chuàng)建一個PokerGameCenter類,也就是游戲中心,采用單例設(shè)計(jì)模式,所以最開始要創(chuàng)建一個靜態(tài)對象
public class PokerGameCenter extends AGameCenter{
    private static PokerGameCenter Instance;//實(shí)例化一個對象
   private int LiveCount;
   private int currentPlayerIndex;
    private PokerGameCenter(){//構(gòu)造方法

    }

    public static PokerGameCenter GetInstance(){
        if(Instance==null){
            synchronized (PokerGameCenter.class){
                if(Instance==null){
                    Instance=new PokerGameCenter();
                }
            }
        }
        return Instance;
    }

    @Override
    protected void initGame() {
        //創(chuàng)建對象
      playerManager=playerManager.GetManager();
      pokerManager=pokerManager.GetManager();
      ante=0;
      totalPlayer=3;
      Bottom=5;
      currentPlayerIndex=1;
      LiveCount=totalPlayer;
      //設(shè)置監(jiān)聽者
      playerManager.setListener(this);
      pokerManager.setListener(this);
        //初始化完畢,成功的計(jì)數(shù)器+1
        setTotalSuccess(getTotalSuccess()+1);
    }

    @Override
    protected void initPokers() {
            pokerManager.InitPokers();
    }

    @Override
    protected void initPlayers() {
          playerManager.InitPlayers();
    }

    @Override
    protected void start() {
        //先扣底注錢
        PlayerManager.GetManager().DeductMoney(Bottom);
        //實(shí)現(xiàn)發(fā)牌
       dealCards();
       //開始下注
        startBets();
    }
    }

4.創(chuàng)建管理類實(shí)現(xiàn)接口回調(diào)

①創(chuàng)建一個PokerManager類來管理一副牌,因?yàn)橛玫氖菃卫O(shè)計(jì)模式,所以需要設(shè)置一個靜態(tài)對象,并且私有化構(gòu)造方法,用接口創(chuàng)建了一個對象Listener來方便將對象創(chuàng)建成功的事件返回給監(jiān)聽者(游戲中心)注:在創(chuàng)建了Poker類之后就可以實(shí)現(xiàn)PokerManager類里面的InitPokers方法。
public class PokerManager {
   private IGameInitListener Listener;

    private static PokerManager manager;
    private  PokerManager(){

    }
    public static PokerManager GetManager(){
        if(manager==null){
            synchronized (PokerManager.class){
                if (manager==null){
                    manager=new PokerManager();
                }
            }
        }
        return manager;
    }

    public void InitPokers(){
        //初始化數(shù)組對象
        pokers=new ArrayList<>();
        //創(chuàng)建撲克牌
        for(String dot: Constants.IPoker.DOTS){
            //這樣就取出來了一張牌,之后選擇花色
           for(Poker.PicType type:Constants.IPoker.PIC_TYPES){
               //創(chuàng)建一張牌
               Poker poker=new Poker(dot,type);
               //添加到數(shù)組中保存
               pokers.add(poker);
           }
        }
        //當(dāng)撲克牌初始化成功就回調(diào)成功的事件給監(jiān)聽者(游戲中心)
        if(Listener!=null){
            Listener.onInitSuccess();
        }
    }

    public void setListener(IGameInitListener listener) {
        Listener = listener;
    }
}
②創(chuàng)建一個PlayerManager類來管理許多玩家,采用的也是單例設(shè)計(jì)模式,其他原理同上
public class PlayerManager {
    private IGameInitListener Listener;
    private static PlayerManager manager;
    private  PlayerManager(){

    }
    public static PlayerManager GetManager(){
        if(manager==null){
            synchronized (PlayerManager.class){
                if (manager==null){
                    manager=new PlayerManager();
                }
            }
        }
        return manager;
    }
    public void InitPlayers(){
        //當(dāng)玩家初始化成功就回調(diào)成功的事件給監(jiān)聽者(游戲中心)
        if(Listener!=null){
            Listener.onInitSuccess();
        }
    }

    //獲取一個玩家
    public Player getPlayer(int index){
         return players.get(index);
    }
    }
    public void setListener(IGameInitListener listener) {
        Listener = listener;
    }
}

5.創(chuàng)建一副牌

  • 先創(chuàng)建一個Constants接口來記錄牌的點(diǎn)數(shù)和花色
public interface Constants {
    interface IPlayer{
        int chips=1000;
    }
    interface IPlayerName{
        String [] NAMES_XING={"趙","錢","孫","李","周","吳","鄭","王","馮"};
        String [] NAMES_MING1={"子","秋","鶴","文","澤","凱","元","建","可"};
        String [] NAMES_MING2={"豪","月","雪","晨","嵐","崢","瑤","欣","陽"};
    }
 
    //撲克使用的常量
     interface IPoker{
         //點(diǎn)數(shù)
        String[] DOTS={"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
        //四種花色,給每一個花色都匹配一個tag值
        Poker.PicType[] PIC_TYPES={Poker.PicType.SPADE,Poker.PicType.HEARTS,
                                   Poker.PicType.CLUBS, Poker.PicType.DIAMONDS};
    }
}
  • 創(chuàng)建一個Poker類,再在這個類里面創(chuàng)建一個內(nèi)部類PicType來管理牌的花色和tag值,tag值是為了方便比較花色大小所以自己給花色賦的值。因?yàn)榛ㄉ枪潭ú蛔兊模钥梢远x四個靜態(tài)變量來表示四種不同的花色。另外構(gòu)造方法是必要的。
public class Poker {
     public String dot;//管理牌的點(diǎn)數(shù)
     public PicType type;//花色對象

    public Poker(String dot, PicType type) {
        this.dot = dot;
        this.type = type;
    }

    public static class PicType{
        public String pic;//管理花色
        public int tag;//花色對應(yīng)的tag值
        //
        public static final PicType SPADE=new PicType("?",4);
        public static final PicType HEARTS=new PicType("?",3);
        public static final PicType CLUBS=new PicType("?",2);
        public static final PicType DIAMONDS=new PicType("?",1);

        public PicType(String pic, int tag) {
            this.pic = pic;
            this.tag = tag;
        }
    }


    @Override
    public String toString() {
       return dot+type.pic;
    }
}

6.創(chuàng)建玩家,自動生成名字

  • 現(xiàn)在可以在AGameCenter里面添加一個變量來記錄人數(shù),并在PokerGamecenter類里面初始化為3
 protected int totalPlayer;//玩家人數(shù)
  • 然后創(chuàng)建一個玩家Player類,里面有很多不同的屬性(名字,編號等),在記錄是否棄牌時,就需要在Constants里面添加一個內(nèi)部接口記錄玩家的狀態(tài)
 interface IPlayerState{
        int HAND=0;//還在玩
        int DISCARD=1;//棄牌
    }
  • 創(chuàng)建一個Player類,里面包含玩家的一些屬性,其中編號,名字和籌碼可以直接知道就可以寫在一個構(gòu)造方法里面,代碼如下
public class Player {
    public int id;//編號
    public String name;//名字
    public int chips;//籌碼
    public Poker poker;//手上的一張牌
    public ArrayList<Poker> pokers;//手上有很多張牌
    public int  playState;//玩家的狀態(tài)

    public Player(int id, String name, int chips) {
        this.id = id;
        this.name = name;
        this.chips = chips;

       playState=Constants.IPlayerState.HAND;//初始化狀態(tài)
    }
}
  • 創(chuàng)建一個Util類來隨機(jī)生成名字,因?yàn)閿?shù)組下標(biāo)必為整數(shù),所以用MAth.abs()函數(shù)取隨機(jī)數(shù)的絕對值
public class Util {
   public static String AutoGeneName(){
        Random random=new Random();
        //姓名的隨機(jī)數(shù)
        int x_index=Math.abs(random.nextInt()%Constants.IPlayerName.NAMES_XING.length);
        int m_index=Math.abs( random.nextInt()%Constants.IPlayerName.NAMES_MING1.length);
        int l_index=Math.abs( random.nextInt()%Constants.IPlayerName.NAMES_MING2.length);

       String f= Constants.IPlayerName.NAMES_XING[x_index]+
        Constants.IPlayerName.NAMES_MING1[m_index]+
        Constants.IPlayerName.NAMES_MING2[l_index];
       return f;
    }
    /**
     *輸出語句
     */
    public static void show (boolean nextLine,boolean needNumber,String...args) {
        StringBuilder builder=new StringBuilder();
        if (needNumber) {
            for (int i = 0; i < args.length; i++) {
                String content = (i + 1) + "." + args[i]+" ";//1.下注2.跟注等
                  builder.append(content);//把數(shù)組里面的都追加過來
            }
        }else{
            for(String content:args){
                builder.append(content+" ");
            }
        }

        if(nextLine){
            System.out.println(builder.toString());
        }else{
            System.out.print(builder.toString());
        }
    }
   //輸出一行不換行,不需要編號的數(shù)據(jù)
   public static void show(String content){
        Util.show(false,false,new String[]{content});
    }
}

7.發(fā)牌

  • 需要先設(shè)置一個底注Bottom,在AGameCenter類里面定義,在PokerGameCenter類里面初始化
protected int Button;//底注
  • 在發(fā)牌之前需要先扣錢,先在Player類里面定義一個扣錢的方法Lost Money,在PlayerManager類里面寫一個DeductMoney方法扣錢,遍歷每個數(shù)組并調(diào)用LostMoney方法
//扣錢
    public void LostMoney(int count){
        chips-=count;
    }
//所有玩家扣錢
    public void DeductMoney(int count){
        for(Player player:players){
             player.LostMoney(count);
        }
    }
  • 開始發(fā)牌,需要在PokerManager類里面添加一個getOnePoker方法來獲取一張牌,因?yàn)榕频捻樞蛞呀?jīng)是亂的,所以每次選擇牌的第一張即可,并且刪除這張牌(以免后面的玩家抽到一樣的牌)。
  • 然后在PokerGameCenter類實(shí)現(xiàn)start方法實(shí)現(xiàn)發(fā)牌,每次發(fā)一張牌,調(diào)用getOnePoker方法,并將這張牌發(fā)給對應(yīng)的人。創(chuàng)建一副牌之后還得打亂順序
 public Poker getOnePoker(){
        //因?yàn)榕埔呀?jīng)被打亂了,所以獲取第一張,再將其刪除即可
        if(pokers.size()>0){
            Poker poker=pokers.get(0);
            //將這張牌從數(shù)組里面刪除
            pokers.remove(0);
            return poker;
        }
       return null;
    }
       //打亂順序-洗牌
        Collections.shuffle(pokers);

8.包裝輸出語句

  • 在下注之前我們要顯示玩家的選擇,先在PokerGameCenter類里面的start方法里面添加一個下注的函數(shù),然后在start方法外實(shí)現(xiàn)這個函數(shù)。在Util類里面寫一個輸出方法,顯示用戶的選擇。(其具體代碼見上)
 public static void show (boolean nextLine,boolean needNumber,String...args) {
        StringBuilder builder=new StringBuilder();
        if (needNumber) {
            for (int i = 0; i < args.length; i++) {
                String content = (i + 1) + "." + args[i]+" ";//1.下注2.跟注等
                  builder.append(content);//把數(shù)組里面的都追加過來
            }
        }else{
            for(String content:args){
                builder.append(content+" ");
            }
        }

        if(nextLine){
            System.out.println(builder.toString());
        }else{
            System.out.print(builder.toString());
        }
    }

9.顯示提示內(nèi)容接收選擇

  • 在PokerGameCenter類我們要實(shí)現(xiàn)startBets方法,顯示內(nèi)容,那我們就在Util類里添加了一個show函數(shù),然后在startBets方法里調(diào)用這個函數(shù).
//輸出一行不換行,不需要編號的數(shù)據(jù)
   public static void show(String content){
        Util.show(false,false,new String[]{content});
    }
  • 在用戶選擇后我們需要寫一個方法來接收用戶的輸入,將這個input方法直接寫在PoerkGameCenter類里面,還需要一個一個方法來發(fā)牌,同樣寫在PoerkGameCenter類里面
 private int  input(int max,int min) {
        while (1 > 0) {
            Scanner scanner = new Scanner(System.in);
            int num = scanner.nextInt();
            if (num >= min || num <= min) {
                return num;
            }
            Util.show("輸入數(shù)據(jù)不正確,請重新輸入");
        }
       private void dealCards () {
            int count = playerManager.getPlayerCount();
            for (int i = 0; i < count; i++) {
                //從撲克中心獲取一張牌
                Poker poker = PokerManager.GetManager().getOnePoker();
                //將這張牌發(fā)給對應(yīng)的人
                Player player = PlayerManager.GetManager().getPlayer(i);
                player.poker = poker;
            }
            System.out.println(playerManager.players);
        }
    }

10.實(shí)現(xiàn)棄牌和下注

  • 某個玩家棄牌后,當(dāng)前在玩人數(shù)減一,同時玩家的索引值加一,并判斷索引值是否超過在玩人數(shù),如果超過,索引值就初始化為1
case 5:
                  //棄牌
                  player.playState= Constants.IPlayerState.DISCARD;
                  //棄牌之后,在玩的玩家數(shù)減一
                  LiveCount--;
               
  • 之后索引值會加一,寫在switch語句后面
 currentPlayerIndex++;
                  if(currentPlayerIndex>totalPlayer){
                      currentPlayerIndex=1;
                  }
  • 在Player類里面添加一個扣錢的方法
//加錢
     public void WinMoney(int count){
        chips+=count;
     }
  • 在PlayerManager類添加一個AwardMoney方法,把錢加給玩家
 public void awardPlayer(int money){
        for(Player player:players){
            if(player.playState==Constants.IPlayerState.HAND){
               player.WinMoney(money);
            }
        }
    }
  • 下注:在PokerGameCenter方法里面添加一個下注方法,然后在caes1里調(diào)用這個函數(shù),因?yàn)槊看蜗伦⒍家壬弦粋€玩家多,所以定義一個變量來記錄上一個玩家下的注。
  //下注
 private void Bet(Player player){
       Util.show("請輸入下注金額:");
      int total= input(player.chips,LastPlayerBet);
      //總金額增加
       ante+=total;
       //扣除這個人的籌碼
       player.LostMoney(total);
     //記錄這次下注金額
     LastPlayerBet=total;
 }

11.實(shí)現(xiàn)不同狀態(tài)的提示和跟注

  • 如果一個人的所有的金幣都少于前一個人的賭注,那么這個時候他只有兩種選擇,一種是all-in,另一種就是棄牌,所以在做選擇前,我們要先比較每一個玩家的所有錢和前一個人的賭注,所以就要分兩種不同的情況,每種情況的顯示與選擇都不同。所以我們在constants接口里另外寫一個接口來作為顯示
 interface IBet{
        String [] NORMAL =new String[]{"下注 2.跟注 3.pall-in 4.比牌 5.棄牌"};
        String [] LESS= new String[]{"all-in,棄牌"};
    }
  • 在startbet里面重新寫過startBet方法
 //下注
    private void startBets(){
        while(LiveCount>1) {
            //判斷當(dāng)前這個人的提示信息
            Player player = playerManager.getPlayer(currentPlayerIndex - 1);
            if (player.chips <= LastPlayerBet) {
                Util.show(true, true, Constants.IBet.LESS);
                //獲取當(dāng)前玩家信息
                Util.show("請" + player.id + "號玩家選擇操作(" + player.name + " $" + player.chips + "):");
                //接收用戶輸入
                int choice = input(2, 1);
                System.out.println(choice);
                switch (choice) {
                    case 1:
                        //all-in
                        break;
                    case 2:
                        //棄牌
                        player.playState = Constants.IPlayerState.DISCARD;
                        //棄牌之后,在玩的玩家數(shù)減一
                        LiveCount--;
                        //當(dāng)前玩家索引指向下一個

                        break;
                }
            } else {
                Util.show(true, true, Constants.IBet.NORMAL);
                //獲取當(dāng)前玩家信息
                Util.show("請" + player.id + "號玩家選擇操作(" + player.name + " $" + player.chips + "):");
                //接收用戶輸入
                int choice = input(2, 1);
                System.out.println(choice);
                switch (choice) {
                    case 1:
                        Bet(player);
                        break;
                    case 2:
                        break;
                    case 3:
                        break;
                    case 4:
                        break;
                    case 5:
                        //棄牌
                        player.playState = Constants.IPlayerState.DISCARD;
                        //棄牌之后,在玩的玩家數(shù)減一
                        LiveCount--;
                        //當(dāng)前玩家索引指向下一個
                }

                break;
            }

 currentPlayerIndex++;
            if(currentPlayerIndex>totalPlayer){
                currentPlayerIndex=1;
            }

        }
        //游戲結(jié)束
        playerManager.awardPlayer(ante);
        System.out.println(playerManager.players);
    }
  • 因?yàn)椴徽撏婕业慕饚挪徽撌嵌嘤谶€是低于前一個人的賭注,都有棄牌這個功能,所以我們另外寫一個函數(shù)來表示棄牌。前面直接調(diào)用這個函數(shù)即可,寫在PokerGameCenter類里面
 //棄牌
      private void giveup(Player player){
          player.playState = Constants.IPlayerState.DISCARD;
          //棄牌之后,在玩的玩家數(shù)減一
          LiveCount--;
      }
  • 跟注我們也可以寫一個函數(shù),然后直接調(diào)用即可,寫在PokerGameCenter類里面
//跟注
    private void follow(Player player){
        //總金額增加
        ante+=LastPlayerBet;
        //扣除這個人的籌碼
        player.LostMoney(LastPlayerBet);
    }

12.實(shí)現(xiàn)all-in

  • 這個我們也是在PokerGameCenter類里面寫一個allin函數(shù)即可
  • 原理:當(dāng)一個人選擇all-in,只需要比較一次,最大的贏。如果有任何一個人選擇了all-in,就要提示后面的人要all-in還是棄牌,只有這兩種選擇。所以我們定義一個變量記錄玩家是否選擇了all-in,并且初始化為false(在InitGame函數(shù)里初始化),再定義一個變量來記錄AllinPosition來記錄all-in的位置,先讓它=currentPlayerIndex(在All-in函數(shù)里賦值)。
   private boolean Allin;
   private int AllinPosition;
  • 在satrtbets函數(shù)里的循環(huán)開始的時候,我們要先判斷currentPlayerIndex與AllinPosition是否相等,然后比較大小,比較大小這個方法寫在Poker類里面
//比較兩張牌的大小
   public int Compare(Poker another){
      if(  this.type.tag>another.type.tag){
          return 1;
      }else{
          return -1;
      }
   }
  • 在PlayerManager類里面添加一個獎勵贏家的方法
public void awardPlayer(int money,int smallestAllinBet) {
        Player max = null;
        for (Player player : players) {
            if (player.playState == Constants.IPlayerState.HAND) {
                if (max == null) {
                    //找到第一個沒有棄牌的人
                    max=player;
                }
                else
                {
              int result=  max.poker.Compare(player.poker);
              if(result==-1){
                  //max記錄最大的那個玩家
                  max=player;
              }
                }
            }
        }
        //讓最大的人贏錢
        max.WinMoney(money);
    }
  • 如果有多個人選擇all-in,定義一個變量記錄最小的下注金額(在PokerGameCenter類里面)
private int smallestAllinBet;
  • 如果有多人all-in,其中有玩家的總金幣多于smallestBet,那么多于的錢就需要返回給玩家,在Player類里面添加一個還錢方法
 //還錢
    public void returnMoney(int couut){
      chips+=couut;
    }
  • 同時在awardPlayer方法里面添加退錢的代碼,首先判斷all-in的人數(shù),再進(jìn)行比較
//沒人all-in
        if(smallestAllinBet==0){
            return;
        }
        //將max之外所有多于all-in的人的錢返還
        int totalReturn=0;
        for(Player player:players){
           //找到?jīng)]有棄牌并且不是當(dāng)前最大的那個人
            if(!player.equals(max)&&player.playState==Constants.IPlayerState.HAND){
                player.returnMoney(player.currentBet-smallestAllinBet);
                totalReturn+=(player.currentBet-smallestAllinBet);
            }
        }
        //從max中退回多余的錢
        max.LostMoney(totalReturn);
  • 因?yàn)樗饕?1這個需要多次使用,所以我們將它寫成一個方法在PokerGameCenter類里面
 private void changeCurrentIndex(){
        //當(dāng)前玩家索引值指向下一個
       currentPlayerIndex++;
       if(currentPlayerIndex>totalPlayer){
           currentPlayerIndex=1;
       }
   }
  • 同時在satrtBet方法也需要修改一下,在獲取一個玩家的信息之后要先判斷其是否棄牌,如果棄牌那么之后所有的case操作都沒必要進(jìn)行
       //判斷當(dāng)前這個人是否已經(jīng)棄牌
            if(player.playState==Constants.IPlayerState.DISCARD){
                //這個人已經(jīng)棄牌,下面的不用做
                changeCurrentIndex();
                continue;
            }
  • 那么all-in函數(shù)的完整代碼如下
 private void allin(Player player){
        if(Allin){
            //比較兩個All-in從值
            if(player.chips<smallestAllinBet){
                smallestAllinBet=player.chips;
            }
        }else {
            //第一個人第一次all-in,記錄當(dāng)前最小值
            Allin=true;
            smallestAllinBet=player.chips;
            //記錄當(dāng)前all-in起始點(diǎn)
            AllinPosition=currentPlayerIndex;
        }
       //當(dāng)前這個人all-in
        player.currentBet=player.chips;
        //總金額
        ante+=player.chips;
        //下注所有
        player.LostMoney(player.chips);
    }
那么我們所有的功能都已經(jīng)實(shí)現(xiàn)了,只需要在主函數(shù)里面創(chuàng)建按一個對象即可

public class MyClass {
    public static void main(String[] args) {
    PokerGameCenter center = PokerGameCenter.GetInstance();
    }
}
之后我們運(yùn)行這段代碼,就會顯示一下內(nèi)容
C5H}Q97{FOHM6(C_0IR1WN8.png

IY9AL1%2WN3AT_Z{3H5FPLK.png
然后選擇你想要的操作即可,這就是我們今天的全部內(nèi)容了,我會將所有的源代碼放在最后
public abstract class  AGameCenter implements IGameInitListener {
    private int totalSuccess;
   protected PlayerManager playerManager;
   protected PokerManager pokerManager;
   protected int ante;//臺面的總金額
   protected int totalPlayer;
   protected int Bottom;//底注

    protected AGameCenter(){
        //初始化游戲本身的數(shù)據(jù)
        initGame();
        //初始化玩家
        initPlayers();
        //初始化撲克
        initPokers();

    }

    protected abstract void initGame();

    protected abstract void initPokers();

    protected abstract void initPlayers();

    protected abstract void start();

    @Override
    public void onInitSuccess() {
        //對成功的計(jì)數(shù)器+1
    setTotalSuccess(getTotalSuccess()+1);
    }

    @Override
    public void onInitFailure() {

    }

    public void setTotalSuccess(int totalSuccess) {
        this.totalSuccess = totalSuccess;
        if(totalSuccess==3){
            start();
        }
    }

    public int getTotalSuccess() {
        return totalSuccess;
    }
}
public interface IGameInitListener {
   void onInitSuccess();
   void onInitFailure();
}
public class PokerGameCenter extends AGameCenter {
    private int LiveCount;
    private static PokerGameCenter Instance;//實(shí)例化一個對象
    private int currentPlayerIndex;
    private int LastPlayerBet;
    private boolean Allin;
    private int AllinPosition;
    private int smallestAllinBet;
    private PokerGameCenter(){

    }

    public static PokerGameCenter GetInstance(){
        if(Instance==null){
            synchronized (PokerGameCenter.class){
                if(Instance==null){
                    Instance=new PokerGameCenter();
                }
            }
        }
        return Instance;
    }

    @Override
    protected void initGame() {
        //創(chuàng)建對象
      playerManager=playerManager.GetManager();
      pokerManager=pokerManager.GetManager();
      ante=0;
      totalPlayer=3;
      Bottom=5;
      Allin=false;
      currentPlayerIndex=1;
      LiveCount=totalPlayer;
      //設(shè)置監(jiān)聽者
      playerManager.setListener(this);
      pokerManager.setListener(this);
        //初始化完畢,成功的計(jì)數(shù)器+1
        setTotalSuccess(getTotalSuccess()+1);
    }

    @Override
    protected void initPokers() {
        pokerManager.InitPokers();
    }

    @Override
    protected void initPlayers() {
        playerManager.InitPlayers(totalPlayer);
    }

    @Override
    protected void start() {
        //先扣底注錢
        PlayerManager.GetManager().DeductMoney(Bottom);
        ante=totalPlayer*Bottom;
        //實(shí)現(xiàn)發(fā)牌
       dealCards();
       //開始下注
        startBets();

    }
    //下注
    private void startBets(){
        while(LiveCount>1) {
            if(currentPlayerIndex==AllinPosition){
                //直接比大小
                break;
            }
            //判斷當(dāng)前這個人的提示信息
            Player player = playerManager.getPlayer(currentPlayerIndex - 1);
            //判斷當(dāng)前這個人是否已經(jīng)棄牌
            if(player.playState==Constants.IPlayerState.DISCARD){
                //這個人已經(jīng)棄牌,下面的不用做
                changeCurrentIndex();
                continue;
            }
            if (player.chips <= LastPlayerBet) {
                Util.show(true, true, Constants.IBet.LESS);
                //獲取當(dāng)前玩家信息
                Util.show("請" + player.id + "號玩家選擇操作(" + player.name + " $" + player.chips + "):");
                //接收用戶輸入
                int choice = input(2, 1);
                System.out.println(choice);
                switch (choice) {
                    case 1:
                        //all-in
                        allin(player);
                        break;
                    case 2:
                        //棄牌
                       giveup(player);
                        break;
                }
            } else {
                Util.show(true, true, Constants.IBet.NORMAL);
                //獲取當(dāng)前玩家信息
                Util.show("請" + player.id + "號玩家選擇操作(" + player.name + " $" + player.chips + "):");
                //接收用戶輸入
                int choice = input(2, 1);
                System.out.println(choice);
                switch (choice) {
                    case 1:
                        //下注
                        Bet(player);
                        break;
                    case 2:
                        //跟注
                     follow(player);
                        break;
                    case 3:
                        //all-in
                        allin(player);
                        break;
                    case 4:
                        //比牌
                        break;
                    case 5:
                        //棄牌
                      giveup(player);
                      break;
                }
                changeCurrentIndex();
            }
        }
        //游戲結(jié)束
        playerManager.awardPlayer(ante,smallestAllinBet);
        System.out.println(playerManager.players);
    }
    //跟注
    private void follow(Player player){
        //總金額增加
        ante+=LastPlayerBet;
        //扣除這個人的籌碼
        player.LostMoney(LastPlayerBet);
    }
//all-in
    private void allin(Player player){
        if(Allin){
            //比較兩個All-in從值
            if(player.chips<smallestAllinBet){
                smallestAllinBet=player.chips;
            }
        }else {
            //第一個人第一次all-in,記錄當(dāng)前最小值
            Allin=true;
            smallestAllinBet=player.chips;
            //記錄當(dāng)前all-in起始點(diǎn)
            AllinPosition=currentPlayerIndex;
        }
       //當(dāng)前這個人all-in
        player.currentBet=player.chips;
        //總金額
        ante+=player.chips;
        //下注所有
        player.LostMoney(player.chips);
    }


    //下注
 private void Bet(Player player){
       Util.show("請輸入下注金額:");
      int total= input(player.chips,LastPlayerBet);
      //總金額增加
       ante+=total;
       //扣除這個人的籌碼
       player.LostMoney(total);
       //記錄這次下注金額
     LastPlayerBet=total;
 }

    private int  input(int max,int min) {
        while (1 > 0) {
            Scanner scanner=new Scanner(System.in);
            int num = scanner.nextInt();
            if (num >= min || num <= min) {
                return num;
            }
            Util.show("輸入數(shù)據(jù)不正確,請重新輸入");
        }
    }
        private void dealCards () {
            int count = playerManager.getPlayerCount();
            for (int i = 0; i < count; i++) {
                //從撲克中心獲取一張牌
                Poker poker = PokerManager.GetManager().getOnePoker();
                //將這張牌發(fā)給對應(yīng)的人
                Player player = PlayerManager.GetManager().getPlayer(i);
                player.poker = poker;
            }
            System.out.println(playerManager.players);
        }

        //棄牌
      private void giveup(Player player){
          player.playState = Constants.IPlayerState.DISCARD;
          //棄牌之后,在玩的玩家數(shù)減一
          LiveCount--;
      }
   private void changeCurrentIndex(){
        //當(dāng)前玩家索引值指向下一個
       currentPlayerIndex++;
       if(currentPlayerIndex>totalPlayer){
           currentPlayerIndex=1;
       }
   }
}
public class Player {
    public int id;//編號
    public String name;//名字
    public int chips;//籌碼
    public Poker poker;//手上的一張牌
    public ArrayList<Poker> pokers;//手上有很多張牌
    public int  playState;//玩家的狀態(tài)
    public int currentBet;//記錄當(dāng)前下注金額

    public Player(int id, String name, int chips) {
        this.id = id;
        this.name = name;
        this.chips = chips;

       playState=Constants.IPlayerState.HAND;//初始化狀態(tài)
    }
    //扣錢
    public void LostMoney(int count){
        chips-=count;
    }

    //加錢
     public void WinMoney(int count){
        chips+=count;
     }

     //還錢
    public void returnMoney(int couut){
      chips+=couut;
    }
    @Override
    public String toString() {
        return "id:"+id+" name "+name+" "+poker.dot+poker.type.pic+"("+chips+")";
    }
}
public class PlayerManager {
    private IGameInitListener Listener;
    private static PlayerManager manager;
    public ArrayList<Player> players;

    private  PlayerManager(){

    }
    public static PlayerManager GetManager(){
        if(manager==null){
            synchronized (PlayerManager.class){
                if (manager==null){
                    manager=new PlayerManager();
                }
            }
        }
        return manager;
    }

    //初始化玩家
    public void InitPlayers(int totalPlayer){
        //創(chuàng)建輸出
       players=new ArrayList<>();
       for(int i=0;i<totalPlayer;i++){
           //獲取玩家名字
          String name=Util.AutoGeneName();

           //創(chuàng)建玩家對象
           Player player=new Player(i+1,name, Constants.IPlayer.chips);
           //保存玩家
           players.add(player);
       }
        //當(dāng)玩家初始化成功就回調(diào)成功的事件給監(jiān)聽者(游戲中心)
        if(Listener!=null){
            Listener.onInitSuccess();
        }
    }
     //所有玩家扣錢
    public void DeductMoney(int count){
        for(Player player:players){
             player.LostMoney(count);
        }
    }
    public int getPlayerCount(){
        return players.size();
    }
    //獲取一個玩家
    public Player getPlayer(int index){
         return players.get(index);
    }

    public void awardPlayer(int money,int smallestAllinBet) {
        Player max = null;
        for (Player player : players) {
            if (player.playState == Constants.IPlayerState.HAND) {
                if (max == null) {
                    //找到第一個沒有棄牌的人
                    max=player;
                }
                else
                {
              int result=  max.poker.Compare(player.poker);
              if(result==-1){
                  //max記錄最大的那個玩家
                  max=player;
              }
                }
            }
        }
        //讓最大的人贏錢
        max.WinMoney(money);
        //沒人all-in
        if(smallestAllinBet==0){
            return;
        }
        //將max之外所有多于all-in的人的錢返還
        int totalReturn=0;
        for(Player player:players){
           //找到?jīng)]有棄牌并且不是當(dāng)前最大的那個人
            if(!player.equals(max)&&player.playState==Constants.IPlayerState.HAND){
                player.returnMoney(player.currentBet-smallestAllinBet);
                totalReturn+=(player.currentBet-smallestAllinBet);
            }
        }
        //從max中退回多余的錢
        max.LostMoney(totalReturn);
    }
    public void setListener(IGameInitListener listener) {
        Listener = listener;
    }
}
public class Poker {
    public String dot;//管理牌的點(diǎn)數(shù)
     public PicType type;//花色對象

    public Poker(String dot, PicType type) {
        this.dot = dot;
        this.type = type;
    }

    public static class PicType{
        public String pic;//管理花色
        public int tag;//花色對應(yīng)的tag值
        //
        public static final PicType SPADE=new PicType("?",4);
        public static final PicType HEARTS=new PicType("?",3);
        public static final PicType CLUBS=new PicType("?",2);
        public static final PicType DIAMONDS=new PicType("?",1);

        public PicType(String pic, int tag) {
            this.pic = pic;
            this.tag = tag;
        }
    }

  //比較兩張牌的大小
   public int Compare(Poker another){
      if(  this.type.tag>another.type.tag){
          return 1;
      }else{
          return -1;
      }
   }
    @Override
    public String toString() {
        return dot+type.pic;
    }
}
public class PokerManager {
   private IGameInitListener Listener;
   private ArrayList<Poker> pokers;
    private static PokerManager manager;
    private  PokerManager(){

    }
    public static PokerManager GetManager(){
        if(manager==null){
            synchronized (PokerManager.class){
                if (manager==null){
                    manager=new PokerManager();
                }
            }
        }
        return manager;
    }

    public void InitPokers(){
       //初始化數(shù)組對象
        pokers=new ArrayList<>();
        //創(chuàng)建撲克牌
        for(String dot: Constants.IPoker.DOTS){
            //這樣就取出來了一張牌,之后選擇花色
           for(Poker.PicType type:Constants.IPoker.PIC_TYPES){
               //創(chuàng)建一張牌
               Poker poker=new Poker(dot,type);
               //添加到數(shù)組中保存
               pokers.add(poker);
           }
        }

        //打亂順序-洗牌
        Collections.shuffle(pokers);

        //當(dāng)撲克牌初始化成功就回調(diào)成功的事件給監(jiān)聽者(游戲中心)
        if(Listener!=null){
            Listener.onInitSuccess();
        }
    }

    public Poker getOnePoker(){
        //因?yàn)榕埔呀?jīng)被打亂了,所以獲取第一張,再將其刪除即可
        if(pokers.size()>0){
            Poker poker=pokers.get(0);
            //將這張牌從數(shù)組里面刪除
            pokers.remove(0);
            return poker;
        }
       return null;
    }

    public void setListener(IGameInitListener listener) {
        Listener = listener;
    }
}
public interface Constants {
    interface IBet{
        String [] NORMAL =new String[]{"下注 2.跟注 3.pall-in 4.比牌 5.棄牌"};
        String [] LESS= new String[]{"all-in,棄牌"};
    }

    interface IPlayer{
        int chips=1000;
    }
    interface IPlayerName{
        String [] NAMES_XING={"趙","錢","孫","李","周","吳","鄭","王","馮"};
        String [] NAMES_MING1={"子","秋","鶴","文","澤","凱","元","建","可"};
        String [] NAMES_MING2={"豪","月","雪","晨","嵐","崢","瑤","欣","陽"};
    }
    interface IPlayerState{
        int HAND=0;//還在玩
        int DISCARD=1;//棄牌
    }

    //撲克使用的常量
     interface IPoker{
         //點(diǎn)數(shù)
        String[] DOTS={"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
        //四種花色,給每一個花色都匹配一個tag值
        Poker.PicType[] PIC_TYPES={Poker.PicType.SPADE,Poker.PicType.HEARTS,
                                   Poker.PicType.CLUBS, Poker.PicType.DIAMONDS};
    }

}
public class Util {
    /**
     * 自動生成名字
     */
    public static String AutoGeneName(){
        Random random=new Random();
        //姓名的隨機(jī)數(shù)
        int x_index=Math.abs(random.nextInt()%Constants.IPlayerName.NAMES_XING.length);
        int m_index=Math.abs( random.nextInt()%Constants.IPlayerName.NAMES_MING1.length);
        int l_index=Math.abs( random.nextInt()%Constants.IPlayerName.NAMES_MING2.length);

       String f= Constants.IPlayerName.NAMES_XING[x_index]+
        Constants.IPlayerName.NAMES_MING1[m_index]+
        Constants.IPlayerName.NAMES_MING2[l_index];
       return f;
    }
    /**
     *輸出語句
     */
    public static void show (boolean nextLine,boolean needNumber,String...args) {
        StringBuilder builder=new StringBuilder();
        if (needNumber) {
            for (int i = 0; i < args.length; i++) {
                String content = (i + 1) + "." + args[i]+" ";//1.下注2.跟注等
                  builder.append(content);//把數(shù)組里面的都追加過來
            }
        }else{
            for(String content:args){
                builder.append(content+" ");
            }
        }

        if(nextLine){
            System.out.println(builder.toString());
        }else{
            System.out.print(builder.toString());
        }
    }
   //輸出一行不換行,不需要編號的數(shù)據(jù)
   public static void show(String content){
        Util.show(false,false,new String[]{content});
    }
}
public class MyClass {
    public static void main(String[] args) {
    PokerGameCenter center = PokerGameCenter.GetInstance();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容