美文网首页
Java实战开发(2)——扑克游戏

Java实战开发(2)——扑克游戏

作者: 让时间走12138 | 来源:发表于2020-04-05 19:46 被阅读0次

    本节内容

    1.游戏架构和功能

    2.游戏功能的具体实现

    注:解说以下代码是按照写代码的过程以及思路顺序来讲解的,所以以文字顺序为主,其中文字附带部分代码。所有的源代码在最末尾。

    一、游戏架构和功能

    (一)游戏过程:

    1.设置参与的人数,并给每个人设置名字(ID号)、序号(某一桌)和初始金币
    2.设置本局消耗金币(底注),相当于选择不同等级的房间
    3.开始玩游戏。每个人下底注(比如5块,10块等),然后给每个人发张牌(或者多张牌,根据游戏规则决定),之后按照顺序下注(①->②->③),当前下注的人可以选择{①下注:自由下注,不能比上个玩家少,也不能多于自己的金币。②跟注:上个玩家下注多少就下注多少。③all-in:自己有多少就下多少。如果他的总金币比上一个玩家下的注要少,那么就按他的注来,并退还前面玩家的下注差(比如1,2号下注20,三号只有all-in15,那么就会退还1,2号5个金币)④比牌(前提是玩法只有一张牌):和另外一位玩家比牌,不管输赢。⑤弃牌:放弃},依次下注,只剩一人时游戏结束
    4.一局游戏结束,显示当前所有玩家的金币信息

    (二)该游戏里的对象

    1.玩家
    2.牌
    3.游戏中心(在哪里玩,QQ或者微信等)
    4.玩家的管理者
    5.扑克的管理者
    6.规则:先比大小,再比花色(黑♠>红♥梅♣>方♦)ASCII值分别为(6,3,5,4)所以用ASCII不太方便,所以我们可以自己给黑红梅方设值

    (三)写代码之前需要以下步骤

    找对象抽类——>理清类与类之间的关系——>UML画图/架构
    具体架构:游戏中心(Game Center)管理Poker Manager和Play Manager类。
    • 由Poker管理一张牌,在Poker类里面有(设置内部类PokerType包含属性dot、pic Type和tag int(给黑红梅方设值,这样在比较花色的时候直接比较tag值即可),内部类之外有pic 和dot 以及compare方法,开牌比较大小)。
    • 由PokerManager来管理一副牌(有一个数组保存所有的牌,有一些方法来初始化一副牌,以及获取一张牌等)。游戏中心(Game Center)发牌的时候就会从PokerManager里面拿牌
    • Player类:包含玩家的编号,姓名,筹码,是否弃牌(使用接口),玩家的一张牌,玩家的副牌,还有方法(赢钱,输钱,下注,退还钱如何计算)
    • PlayManager类:包含初始化所有玩家,获取一个玩家等方法
    • Game Center类:包含属性当前台面上的总金额,button(每局的赌注),CurrentPlayer(当前玩家),还有其他类的对象。一些方法,初始化数据,开始游戏,游戏结束。
    • 定义一个iGame接口来判断Game Center类里面初始化是否成功,并回调数据给游戏中心

    二、扑克游戏具体实现

    1.创建AGameCenter的抽象类和单例,直接在构造函数里面设计好执行的顺序,先初始化数据,然后是玩家,最后是扑克,后面补充它们的抽象方法和其他方法
    public abstract class  AGameCenter implements IGameInitListener{
        private int totalSuccess;
       protected PlayerManager playerManager;
       protected PokerManager pokerManager;
       protected int ante;//台面的总金额
       protected int totalPlayer;//玩家人数
       protected int Button;//底注
        protected AGameCenter(){
          //初始化游戏本身的数据
            initGame();
            //初始化玩家
            initPlayers();
            //初始化扑克
            initPokers();
        }
    
        protected abstract void initGame();
    
        protected abstract void initPokers();
    
        protected abstract void initPlayers();
    
        protected abstract void start();
    
        @Override
        public void onInitSuccess() {
            //对成功的计数器+1
        setTotalSuccess(getTotalSuccess()+1);
        }
    
        @Override
        public void onInitFailure() {
    
        }
        public void setTotalSuccess(int totalSuccess) {
            this.totalSuccess = totalSuccess;
            if(totalSuccess==3){//因为要初始化三次,所以通过totalSuccess是不是等于三来判断初始化是否成功
                start();
            }
        }
    
        public int getTotalSuccess() {
            return totalSuccess;
        }
    }
    
    2.创建一个IGameInitListener的接口来判断初始化是否成功
    public interface IGameInitListener {
       void onInitSuccess();
       void onInitFailure();
    }
    
    3.创建一个PokerGameCenter类,也就是游戏中心,采用单例设计模式,所以最开始要创建一个静态对象
    public class PokerGameCenter extends AGameCenter{
        private static PokerGameCenter Instance;//实例化一个对象
       private int LiveCount;
       private int currentPlayerIndex;
        private PokerGameCenter(){//构造方法
    
        }
    
        public static PokerGameCenter GetInstance(){
            if(Instance==null){
                synchronized (PokerGameCenter.class){
                    if(Instance==null){
                        Instance=new PokerGameCenter();
                    }
                }
            }
            return Instance;
        }
    
        @Override
        protected void initGame() {
            //创建对象
          playerManager=playerManager.GetManager();
          pokerManager=pokerManager.GetManager();
          ante=0;
          totalPlayer=3;
          Bottom=5;
          currentPlayerIndex=1;
          LiveCount=totalPlayer;
          //设置监听者
          playerManager.setListener(this);
          pokerManager.setListener(this);
            //初始化完毕,成功的计数器+1
            setTotalSuccess(getTotalSuccess()+1);
        }
    
        @Override
        protected void initPokers() {
                pokerManager.InitPokers();
        }
    
        @Override
        protected void initPlayers() {
              playerManager.InitPlayers();
        }
    
        @Override
        protected void start() {
            //先扣底注钱
            PlayerManager.GetManager().DeductMoney(Bottom);
            //实现发牌
           dealCards();
           //开始下注
            startBets();
        }
        }
    

    4.创建管理类实现接口回调

    ①创建一个PokerManager类来管理一副牌,因为用的是单例设计模式,所以需要设置一个静态对象,并且私有化构造方法,用接口创建了一个对象Listener来方便将对象创建成功的事件返回给监听者(游戏中心)注:在创建了Poker类之后就可以实现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(){
            //初始化数组对象
            pokers=new ArrayList<>();
            //创建扑克牌
            for(String dot: Constants.IPoker.DOTS){
                //这样就取出来了一张牌,之后选择花色
               for(Poker.PicType type:Constants.IPoker.PIC_TYPES){
                   //创建一张牌
                   Poker poker=new Poker(dot,type);
                   //添加到数组中保存
                   pokers.add(poker);
               }
            }
            //当扑克牌初始化成功就回调成功的事件给监听者(游戏中心)
            if(Listener!=null){
                Listener.onInitSuccess();
            }
        }
    
        public void setListener(IGameInitListener listener) {
            Listener = listener;
        }
    }
    
    ②创建一个PlayerManager类来管理许多玩家,采用的也是单例设计模式,其他原理同上
    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(){
            //当玩家初始化成功就回调成功的事件给监听者(游戏中心)
            if(Listener!=null){
                Listener.onInitSuccess();
            }
        }
    
        //获取一个玩家
        public Player getPlayer(int index){
             return players.get(index);
        }
        }
        public void setListener(IGameInitListener listener) {
            Listener = listener;
        }
    }
    

    5.创建一副牌

    • 先创建一个Constants接口来记录牌的点数和花色
    public interface Constants {
        interface IPlayer{
            int chips=1000;
        }
        interface IPlayerName{
            String [] NAMES_XING={"赵","钱","孙","李","周","吴","郑","王","冯"};
            String [] NAMES_MING1={"子","秋","鹤","文","泽","凯","元","建","可"};
            String [] NAMES_MING2={"豪","月","雪","晨","岚","峥","瑶","欣","阳"};
        }
     
        //扑克使用的常量
         interface IPoker{
             //点数
            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};
        }
    }
    
    • 创建一个Poker类,再在这个类里面创建一个内部类PicType来管理牌的花色和tag值,tag值是为了方便比较花色大小所以自己给花色赋的值。因为花色是固定不变的,所以可以定义四个静态变量来表示四种不同的花色。另外构造方法是必要的。
    public class Poker {
         public String dot;//管理牌的点数
         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;//花色对应的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.创建玩家,自动生成名字

    • 现在可以在AGameCenter里面添加一个变量来记录人数,并在PokerGamecenter类里面初始化为3
     protected int totalPlayer;//玩家人数
    
    • 然后创建一个玩家Player类,里面有很多不同的属性(名字,编号等),在记录是否弃牌时,就需要在Constants里面添加一个内部接口记录玩家的状态
     interface IPlayerState{
            int HAND=0;//还在玩
            int DISCARD=1;//弃牌
        }
    
    • 创建一个Player类,里面包含玩家的一些属性,其中编号,名字和筹码可以直接知道就可以写在一个构造方法里面,代码如下
    public class Player {
        public int id;//编号
        public String name;//名字
        public int chips;//筹码
        public Poker poker;//手上的一张牌
        public ArrayList<Poker> pokers;//手上有很多张牌
        public int  playState;//玩家的状态
    
        public Player(int id, String name, int chips) {
            this.id = id;
            this.name = name;
            this.chips = chips;
    
           playState=Constants.IPlayerState.HAND;//初始化状态
        }
    }
    
    • 创建一个Util类来随机生成名字,因为数组下标必为整数,所以用MAth.abs()函数取随机数的绝对值
    public class Util {
       public static String AutoGeneName(){
            Random random=new Random();
            //姓名的随机数
            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);//把数组里面的都追加过来
                }
            }else{
                for(String content:args){
                    builder.append(content+" ");
                }
            }
    
            if(nextLine){
                System.out.println(builder.toString());
            }else{
                System.out.print(builder.toString());
            }
        }
       //输出一行不换行,不需要编号的数据
       public static void show(String content){
            Util.show(false,false,new String[]{content});
        }
    }
    

    7.发牌

    • 需要先设置一个底注Bottom,在AGameCenter类里面定义,在PokerGameCenter类里面初始化
    protected int Button;//底注
    
    • 在发牌之前需要先扣钱,先在Player类里面定义一个扣钱的方法Lost Money,在PlayerManager类里面写一个DeductMoney方法扣钱,遍历每个数组并调用LostMoney方法
    //扣钱
        public void LostMoney(int count){
            chips-=count;
        }
    
    //所有玩家扣钱
        public void DeductMoney(int count){
            for(Player player:players){
                 player.LostMoney(count);
            }
        }
    
    • 开始发牌,需要在PokerManager类里面添加一个getOnePoker方法来获取一张牌,因为牌的顺序已经是乱的,所以每次选择牌的第一张即可,并且删除这张牌(以免后面的玩家抽到一样的牌)。
    • 然后在PokerGameCenter类实现start方法实现发牌,每次发一张牌,调用getOnePoker方法,并将这张牌发给对应的人。创建一副牌之后还得打乱顺序
     public Poker getOnePoker(){
            //因为牌已经被打乱了,所以获取第一张,再将其删除即可
            if(pokers.size()>0){
                Poker poker=pokers.get(0);
                //将这张牌从数组里面删除
                pokers.remove(0);
                return poker;
            }
           return null;
        }
           //打乱顺序-洗牌
            Collections.shuffle(pokers);
    

    8.包装输出语句

    • 在下注之前我们要显示玩家的选择,先在PokerGameCenter类里面的start方法里面添加一个下注的函数,然后在start方法外实现这个函数。在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);//把数组里面的都追加过来
                }
            }else{
                for(String content:args){
                    builder.append(content+" ");
                }
            }
    
            if(nextLine){
                System.out.println(builder.toString());
            }else{
                System.out.print(builder.toString());
            }
        }
    

    9.显示提示内容接收选择

    • 在PokerGameCenter类我们要实现startBets方法,显示内容,那我们就在Util类里添加了一个show函数,然后在startBets方法里调用这个函数.
    //输出一行不换行,不需要编号的数据
       public static void show(String content){
            Util.show(false,false,new String[]{content});
        }
    
    • 在用户选择后我们需要写一个方法来接收用户的输入,将这个input方法直接写在PoerkGameCenter类里面,还需要一个一个方法来发牌,同样写在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("输入数据不正确,请重新输入");
            }
           private void dealCards () {
                int count = playerManager.getPlayerCount();
                for (int i = 0; i < count; i++) {
                    //从扑克中心获取一张牌
                    Poker poker = PokerManager.GetManager().getOnePoker();
                    //将这张牌发给对应的人
                    Player player = PlayerManager.GetManager().getPlayer(i);
                    player.poker = poker;
                }
                System.out.println(playerManager.players);
            }
        }
    
    

    10.实现弃牌和下注

    • 某个玩家弃牌后,当前在玩人数减一,同时玩家的索引值加一,并判断索引值是否超过在玩人数,如果超过,索引值就初始化为1
    case 5:
                      //弃牌
                      player.playState= Constants.IPlayerState.DISCARD;
                      //弃牌之后,在玩的玩家数减一
                      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里调用这个函数,因为每次下注都要比上一个玩家多,所以定义一个变量来记录上一个玩家下的注。
      //下注
     private void Bet(Player player){
           Util.show("请输入下注金额:");
          int total= input(player.chips,LastPlayerBet);
          //总金额增加
           ante+=total;
           //扣除这个人的筹码
           player.LostMoney(total);
         //记录这次下注金额
         LastPlayerBet=total;
     }
    

    11.实现不同状态的提示和跟注

    • 如果一个人的所有的金币都少于前一个人的赌注,那么这个时候他只有两种选择,一种是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) {
                //判断当前这个人的提示信息
                Player player = playerManager.getPlayer(currentPlayerIndex - 1);
                if (player.chips <= LastPlayerBet) {
                    Util.show(true, true, Constants.IBet.LESS);
                    //获取当前玩家信息
                    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;
                            //弃牌之后,在玩的玩家数减一
                            LiveCount--;
                            //当前玩家索引指向下一个
    
                            break;
                    }
                } else {
                    Util.show(true, true, Constants.IBet.NORMAL);
                    //获取当前玩家信息
                    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;
                            //弃牌之后,在玩的玩家数减一
                            LiveCount--;
                            //当前玩家索引指向下一个
                    }
    
                    break;
                }
    
     currentPlayerIndex++;
                if(currentPlayerIndex>totalPlayer){
                    currentPlayerIndex=1;
                }
    
            }
            //游戏结束
            playerManager.awardPlayer(ante);
            System.out.println(playerManager.players);
        }
    
    • 因为不论玩家的金币不论是多于还是低于前一个人的赌注,都有弃牌这个功能,所以我们另外写一个函数来表示弃牌。前面直接调用这个函数即可,写在PokerGameCenter类里面
     //弃牌
          private void giveup(Player player){
              player.playState = Constants.IPlayerState.DISCARD;
              //弃牌之后,在玩的玩家数减一
              LiveCount--;
          }
    
    • 跟注我们也可以写一个函数,然后直接调用即可,写在PokerGameCenter类里面
    //跟注
        private void follow(Player player){
            //总金额增加
            ante+=LastPlayerBet;
            //扣除这个人的筹码
            player.LostMoney(LastPlayerBet);
        }
    

    12.实现all-in

    • 这个我们也是在PokerGameCenter类里面写一个allin函数即可
    • 原理:当一个人选择all-in,只需要比较一次,最大的赢。如果有任何一个人选择了all-in,就要提示后面的人要all-in还是弃牌,只有这两种选择。所以我们定义一个变量记录玩家是否选择了all-in,并且初始化为false(在InitGame函数里初始化),再定义一个变量来记录AllinPosition来记录all-in的位置,先让它=currentPlayerIndex(在All-in函数里赋值)。
       private boolean Allin;
       private int AllinPosition;
    
    • 在satrtbets函数里的循环开始的时候,我们要先判断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的人数,再进行比较
    //没人all-in
            if(smallestAllinBet==0){
                return;
            }
            //将max之外所有多于all-in的人的钱返还
            int totalReturn=0;
            for(Player player:players){
               //找到没有弃牌并且不是当前最大的那个人
                if(!player.equals(max)&&player.playState==Constants.IPlayerState.HAND){
                    player.returnMoney(player.currentBet-smallestAllinBet);
                    totalReturn+=(player.currentBet-smallestAllinBet);
                }
            }
            //从max中退回多余的钱
            max.LostMoney(totalReturn);
    
    • 因为索引值+1这个需要多次使用,所以我们将它写成一个方法在PokerGameCenter类里面
     private void changeCurrentIndex(){
            //当前玩家索引值指向下一个
           currentPlayerIndex++;
           if(currentPlayerIndex>totalPlayer){
               currentPlayerIndex=1;
           }
       }
    
    • 同时在satrtBet方法也需要修改一下,在获取一个玩家的信息之后要先判断其是否弃牌,如果弃牌那么之后所有的case操作都没必要进行
           //判断当前这个人是否已经弃牌
                if(player.playState==Constants.IPlayerState.DISCARD){
                    //这个人已经弃牌,下面的不用做
                    changeCurrentIndex();
                    continue;
                }
    
    • 那么all-in函数的完整代码如下
     private void allin(Player player){
            if(Allin){
                //比较两个All-in从值
                if(player.chips<smallestAllinBet){
                    smallestAllinBet=player.chips;
                }
            }else {
                //第一个人第一次all-in,记录当前最小值
                Allin=true;
                smallestAllinBet=player.chips;
                //记录当前all-in起始点
                AllinPosition=currentPlayerIndex;
            }
           //当前这个人all-in
            player.currentBet=player.chips;
            //总金额
            ante+=player.chips;
            //下注所有
            player.LostMoney(player.chips);
        }
    
    那么我们所有的功能都已经实现了,只需要在主函数里面创建按一个对象即可
    
    public class MyClass {
        public static void main(String[] args) {
        PokerGameCenter center = PokerGameCenter.GetInstance();
        }
    }
    
    之后我们运行这段代码,就会显示一下内容
    C5H}Q97{FOHM6(C_0IR1WN8.png
    IY9AL1%2WN3AT_Z{3H5FPLK.png
    然后选择你想要的操作即可,这就是我们今天的全部内容了,我会将所有的源代码放在最后
    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(){
            //初始化游戏本身的数据
            initGame();
            //初始化玩家
            initPlayers();
            //初始化扑克
            initPokers();
    
        }
    
        protected abstract void initGame();
    
        protected abstract void initPokers();
    
        protected abstract void initPlayers();
    
        protected abstract void start();
    
        @Override
        public void onInitSuccess() {
            //对成功的计数器+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;//实例化一个对象
        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() {
            //创建对象
          playerManager=playerManager.GetManager();
          pokerManager=pokerManager.GetManager();
          ante=0;
          totalPlayer=3;
          Bottom=5;
          Allin=false;
          currentPlayerIndex=1;
          LiveCount=totalPlayer;
          //设置监听者
          playerManager.setListener(this);
          pokerManager.setListener(this);
            //初始化完毕,成功的计数器+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;
            //实现发牌
           dealCards();
           //开始下注
            startBets();
    
        }
        //下注
        private void startBets(){
            while(LiveCount>1) {
                if(currentPlayerIndex==AllinPosition){
                    //直接比大小
                    break;
                }
                //判断当前这个人的提示信息
                Player player = playerManager.getPlayer(currentPlayerIndex - 1);
                //判断当前这个人是否已经弃牌
                if(player.playState==Constants.IPlayerState.DISCARD){
                    //这个人已经弃牌,下面的不用做
                    changeCurrentIndex();
                    continue;
                }
                if (player.chips <= LastPlayerBet) {
                    Util.show(true, true, Constants.IBet.LESS);
                    //获取当前玩家信息
                    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);
                    //获取当前玩家信息
                    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();
                }
            }
            //游戏结束
            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,记录当前最小值
                Allin=true;
                smallestAllinBet=player.chips;
                //记录当前all-in起始点
                AllinPosition=currentPlayerIndex;
            }
           //当前这个人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("输入数据不正确,请重新输入");
            }
        }
            private void dealCards () {
                int count = playerManager.getPlayerCount();
                for (int i = 0; i < count; i++) {
                    //从扑克中心获取一张牌
                    Poker poker = PokerManager.GetManager().getOnePoker();
                    //将这张牌发给对应的人
                    Player player = PlayerManager.GetManager().getPlayer(i);
                    player.poker = poker;
                }
                System.out.println(playerManager.players);
            }
    
            //弃牌
          private void giveup(Player player){
              player.playState = Constants.IPlayerState.DISCARD;
              //弃牌之后,在玩的玩家数减一
              LiveCount--;
          }
       private void changeCurrentIndex(){
            //当前玩家索引值指向下一个
           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;//玩家的状态
        public int currentBet;//记录当前下注金额
    
        public Player(int id, String name, int chips) {
            this.id = id;
            this.name = name;
            this.chips = chips;
    
           playState=Constants.IPlayerState.HAND;//初始化状态
        }
        //扣钱
        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){
            //创建输出
           players=new ArrayList<>();
           for(int i=0;i<totalPlayer;i++){
               //获取玩家名字
              String name=Util.AutoGeneName();
    
               //创建玩家对象
               Player player=new Player(i+1,name, Constants.IPlayer.chips);
               //保存玩家
               players.add(player);
           }
            //当玩家初始化成功就回调成功的事件给监听者(游戏中心)
            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){
               //找到没有弃牌并且不是当前最大的那个人
                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;//管理牌的点数
         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;//花色对应的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(){
           //初始化数组对象
            pokers=new ArrayList<>();
            //创建扑克牌
            for(String dot: Constants.IPoker.DOTS){
                //这样就取出来了一张牌,之后选择花色
               for(Poker.PicType type:Constants.IPoker.PIC_TYPES){
                   //创建一张牌
                   Poker poker=new Poker(dot,type);
                   //添加到数组中保存
                   pokers.add(poker);
               }
            }
    
            //打乱顺序-洗牌
            Collections.shuffle(pokers);
    
            //当扑克牌初始化成功就回调成功的事件给监听者(游戏中心)
            if(Listener!=null){
                Listener.onInitSuccess();
            }
        }
    
        public Poker getOnePoker(){
            //因为牌已经被打乱了,所以获取第一张,再将其删除即可
            if(pokers.size()>0){
                Poker poker=pokers.get(0);
                //将这张牌从数组里面删除
                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{
             //点数
            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();
            //姓名的随机数
            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);//把数组里面的都追加过来
                }
            }else{
                for(String content:args){
                    builder.append(content+" ");
                }
            }
    
            if(nextLine){
                System.out.println(builder.toString());
            }else{
                System.out.print(builder.toString());
            }
        }
       //输出一行不换行,不需要编号的数据
       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();
        }
    }
    

    相关文章

      网友评论

          本文标题:Java实战开发(2)——扑克游戏

          本文链接:https://www.haomeiwen.com/subject/pdoyuhtx.html