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

2020-04-05  本文已影响0人  让时间走12138

本节内容

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();
    }
}
上一篇下一篇

猜你喜欢

热点阅读