之前说好的一定要研究透彻扑克牌项目,小弟说到做到。在下面我将把每一段代码的功能都尽可能描述清楚,一方面是帮助我理解这个项目的执行过程,另一方面也是希望能对大家有所帮助。
一.总
AGameCenter是抽象类并实现IGameInitListener接口,主要是用来“被继承”
Constants接口里面保存一些常量,比如玩家姓名,扑克牌花色等等
IGameInitListener是接口,用来监听初始化是否成功。
PokerGameCenter继承AGameCenter,主要负责控制整个游戏的进行
Util是工具类
Player是玩家类,里面主要有单个玩家的数据信息和操作
PlayerManager是玩家管理类,里面主要涉及操作控制所有玩家的操作
Poker是扑克类,里面主要是涉及单个扑克的操作
PokerManager是扑克管理类,主要涉及操作控制所有扑克的操作。
Main里面是程序入口(这里放的包的不合适,还望大家谅解)
二.分
为了大家能够容易看懂,我们由简到难,一个类一个类地进行说明讲解
1.Constants接口
代码如下
package game;
import Poker1.Poker;
import Poker1.Poker.PicType;
//常量
public interface Constants {
//下面是玩家提示说明
interface IBet{
String[] NORMAL = new String[] {"下注","跟注","all-in","比牌","弃牌"};//正常情况
String[] LESS = new String[] {"all-in","弃牌"};
}
//默认筹码是1000
interface IPlayer{
int CHAPS = 1000;//筹码
}
//玩家姓名常量
interface IPlayerName{
//姓名
String[] NAMES_XING = {"王","李","张","彭"};
String[] NAMES_MING_M = {"宏","涛","国","东","建","强"};
String[] NAMES_MING_L = {"高","晓","博","督","黎"};
}
//玩家状态常量
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"};//点数
//里面保存了四个花色对象,具体看后面说明
Poker.PicType[] PIC_TYPES = {
Poker.PicType.SPADE,Poker.PicType.HRARTS,
Poker.PicType.CLUBS,Poker.PicType.DIAMONDS
};
}
}
这个类总体理解起来不难,重点留意一下IPlayerName即玩家姓名常量和IPoker这两个接口,具体什么功能后面会说。
既然涉及Poker类里面的东西,那下面就介绍Poker
2.Poker类
package Poker1;
import game.Constants;
public class Poker {
public String dot;//牌的点数
public PicType type;//花色对象,这里花色是用了内部类来保存的,类的名称是PicType
//构造方法
public Poker(String dot,PicType type) {
this.dot = dot;
this.type = type;
}
//难点一
public static class PicType{
public String pic;//花色
public int tag;//花色对应的tag值
//由于是涉及花色,所以应该是不可更改的,所以都用final修饰
public static final PicType SPADE = new PicType("♠黑桃",4);//黑桃
public static final PicType HRARTS = 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 compareTo(Poker another) {
int index_m = Util.indexOfObject(this.dot,Constants.IPoker.DOTS);
int index_a = Util.indexOfObject(another.dot,Constants.IPoker.DOTS);
if(index_a == index_m) {
if(this.type.tag > another.type.tag) {
return 1;
}else {
return -1;
}
}else {
if(index_m > index_a) {
return 1;
}else {
return -1;
}
}
}
public String toString() {
return dot+type.pic;
}
}
这个类有两个难点,大家不要急,在这里我会尽可能解释明白
难点①,内部类PicType:
由于比花色是无法直接比较的,所以我们为每个花色都设置了一种映射关系,即黑桃-4 红桃-3 梅花-2 方片-1,把它们封装在内部类PicType中,就可以实现花色的比较。
如何实现?
以SPADE为例,这是PicType的一个对象,这个对象里面有两个数据成员,一个是pic,一个是tag,其中tag就是其映射的数字4,通过SPADE.tag可以取出tag,进而可以和其他花色的tag进行比较。
难点②比较两张牌的大小:
这里使用了Util类里面的一个方法,我在这里先把这个方法单独拿出来
public static int indexOfObject(String object, String ...array){
for (int i = 0; i < array.length; i++){
if (array[i].equals(object)){
return i;
}
}
return -1;
}
这个方法有两个参数,第一个参数String类的object其实传的是这张牌的点数(从这个方法的调用可以看出),然后第二个参数array其实传的是Constants类里面的DOTS数组,这个数组是所有点数,按照从小到大的顺序来排列的,这一点尤为重要,其实也是为什么传递这个数组的原因。因为牌游戏的点数比大小不是简单的2<3,其中还涉及A和JQK这些的比较,而这些不是数字,所以无法与纯数字2345678910直接比较,所以就需要借用它们所在数组的索引值来比较大小,而这个indexOfObject方法其实就是的到这个点数的索引值。
解释到这里,indexOfObject函数就可以先扔一边了,继续看Poker类,我们用了两个变量index_m和index_a来分别保存此牌和另外一张牌点数的索引值。比较两张牌的话,肯定首先比较点数,如果两张牌索引值一样,那么就说明它们点数相同,点数相同就要比较花色,如何比较花色呢?其实前面已经说了,分别取出这两张牌花色的tag值来代替花色进行比较,如果这张牌的花色大,就返回1,如果另外一张牌的花色大,就返回-1.
如果点数不相同,就更好说了,如果这张牌的点数大,就返回1,如果另外一张牌的点数大,就返回-1
3.PokerManager类
package Poker1;
import java.util.*;
import game.Constants;
import game.IGameInitListener;
public class PokerManager {
//监听器,这个东西的功能我一直很迷糊,知道前几天才理解透彻,在这里与大家分享
private IGameInitListener listener;
private ArrayList<Poker> pokers;//保存扑克牌
//创建单例
private static PokerManager manager;//注意这里要是静态
private PokerManager() {
}
//获得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) {
//难点一:type对象是Poker.PicType类的一个对象,这个对象从Constants.IPoker.PIC_TYPES数组里面去取
for(Poker.PicType type: Constants.IPoker.PIC_TYPES) {
//创建一张牌
Poker poker = new Poker(dot,type);
//添加到数组中保存
pokers.add(poker);
}
}
//打乱顺序
Collections.shuffle(pokers);
//输出牌
//System.out.println(pokers);
//当扑克牌初始化成功,就回调成功的事件给listener
if(listener != null) {//难点二:为什么这里listener不是null了呢?后面会给大家做详细解释的。
//成功的计数加1
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) {
this.listener = listener;
}
}
这个类难点相对多一点,不过大家也不用慌,我还是会一一地详细解释的
难点①:创建扑克牌。
其实这里的操作也不是很难理解:利用两个for循环,先确定点数,然后分别给每个点数附上花色。但是稍微有点难度的是这里的增强for循环和PIC_TYPES数组。这个数组里面存了四个元素,这四个元素都是花色对象,而花色对象的类型是Poker.PicType类型,所以代码就出来了。
难点②:为何listener不是空的呢?我也没看见对它进行赋值呀?
关于这一点,得需要后面的类介绍完了才能解释。
难点③:为什么取出这张牌了之后要把这张牌删除呢?
就跟实际玩牌一样,一个玩家取出来了一张牌,那么牌堆儿里面就没有那张牌了。很好理解
难点④:setListener函数是用来干嘛的?
和难点②一样,后面都会有解释
4.Player
package player;
import java.util.*;
import Poker1.Poker;
import game.Constants;
public class Player {
public int id;//编号
public String name;//名字
public int chip;//筹码
public Poker poker;//玩家手上的一张牌
public ArrayList<Poker> pokers;//多张牌
public int playerState;//游戏状态
public int currentBet;//当前下注金额
//构造方法
public Player(int id,String name,int chip) {
this.id = id;
this.name = name;
this.chip = chip;
//初始化状态
playerState = Constants.IPlayerState.HAND;
}
//扣钱方法
public void lostMoney(int count) {
chip -= count;
}
//加钱方法
public void winMoney(int count) {
chip += count;
}
public String toString() {
return "id:"+id+" name:"+name+" "+poker.dot+poker.type.pic+"("+chip+")";
}
//退钱方法
public void returnMoney(int count) {
chip += count;
}
}
这个类无难点,继续往下说
5.PlayerManager
package player;
import java.util.*;
import game.*;
public class PlayerManager {
private IGameInitListener listener;
public ArrayList<Player> players;
//创建单例
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(int totalPlayer) {
//创建玩家数组
players = new ArrayList<>();
for(int i = 0;i < totalPlayer;i++) {
//获取玩家名字(难点一:如何获取的玩家名字)
String name = Util.autoGenerateName();
//创建玩家对象
Player player = new Player(i+1,name,Constants.IPlayer.CHAPS);
//保存玩家
players.add(player);
}
//当玩家初始化成功,就回调成功的事件给监听者/listener/游戏中心(难点二:listener为何是null?)
if(listener != null) {
listener.onInitSuccess();
}
}
//获取玩家人数
public int getPlayerCount() {
return players.size();
}
//扣底注钱的方法(打底),所有玩家都扣
public void deDuctMoney(int count) {
for(Player player:players) {
player.lostMoney(count);
}
}
//获取一个玩家
public Player getPlayer(int index) {
return players.get(index);
}
//难点二:money,smallestAllinBet是什么意思,整个方法的逻辑是什么?
public void awardPlayer(int money,int smallestAllinBet) {
Player max = null;
for(Player player:players) {
if(player.playerState == Constants.IPlayerState.HAND) {
if(max == null) {
//找到第一个手上有牌的人/没有弃牌的人
max = player;
}else {
int result = max.poker.compareTo(player.poker);
if(result == -1) {
//max对应的牌比player的牌要小
//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.playerState == Constants.IPlayerState.HAND) {
player.returnMoney(player.currentBet-smallestAllinBet);//此人当前下注金额减去最小的all-in值就是需要返还的金额
totalReturn += (player.currentBet - smallestAllinBet);
}
}
//从max中退回多余的钱(max多拿了totalReturn)
max.lostMoney(totalReturn);
}
//难点三:这个方法的意义?
public void setListener(IGameInitListener listener) {
this.listener = listener;
}
}
难点①:获取玩家姓名方法如何执行的。
首先来看Util类中获取姓名的方法
public static String autoGenerateName() {
Random random = new Random();
//姓名的随机数
int f_index = Math.abs(random.nextInt()%Constants.IPlayerName.NAMES_XING.length);
int m_index = Math.abs(random.nextInt()%Constants.IPlayerName.NAMES_MING_M.length);
int l_index = Math.abs(random.nextInt()%Constants.IPlayerName.NAMES_MING_L.length);
String f = Constants.IPlayerName.NAMES_XING[f_index];
String m = Constants.IPlayerName.NAMES_MING_M[m_index];
String l = Constants.IPlayerName.NAMES_MING_L[l_index];
return f+m+l;
}
random.nextInt()%Constants.IPlayerName.NAMES_XING.length的作用是想要获取数组所有索引值之一,但是有负数。Math.abs(x) 函数返回指定数字 “x“ 的绝对值。,也就是获得了真真正正的索引值,即0~数组最后一个元素的索引值。然后再将随机产生的姓,中间的名,和末尾的名,拼接起来(假设名字有三个汉字),就生成了名字。
难点②:public void awardPlayer(int money,int smallestAllinBet)这个方法是用来干啥的?
后面会有相应解释
难点③: public void setListener(IGameInitListener listener) 有什么作用?
和PokerManager一样的难点,后面都有相应解释,请大家耐心阅读。
6.PokerGameCenter
package game;
import java.util.Scanner;
import Poker1.Poker;
import Poker1.PokerManager;
import player.Player;
import player.PlayerManager;
public class PokerGameCenter extends AGameCenter{
private int liveCount;//生存的玩家数目
private int currentPlayerIndex;//当前玩家索引值
private int lastPlayerBet;//上一个玩家下注的金额
private boolean isAllIn = false;//是否all-in
private int allInPosition;//从哪开始all-in的
private int smallestAllinBet;
//创建单例
private static PokerGameCenter instance;
private PokerGameCenter() {
}
public static PokerGameCenter getInstance() {
if(instance == null) {
synchronized(AGameCenter.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;//底注
liveCount = totalPlayer;
currentPlayerIndex = 1;
//设置监听者
playerManager.setListener(this);//这里就知道为什么listener不是null了
pokerManager.setListener(this);
//初始化完毕,成功的计数器+1
setTotalSuccess(getTotalSuccess() + 1);//难点一:这个计数器是干嘛的?
}
@Override
protected void initPokers() {
//System.out.println("initpokers!");
pokerManager.initPokers();
}
@Override
protected void initPlayers() {
//System.out.println("initPlayers!");
playerManager.initPlayers(totalPlayer);
}
@Override
protected void start() {
System.out.println("start!");
//1.先扣底注钱
playerManager.deDuctMoney(bottom);
ante = totalPlayer * bottom;
//2.发牌
dealCards();
//3.开始下注
startBets();
}
//发牌方法
private void dealCards() {
int count = playerManager.getPlayerCount();
for(int i = 0;i < count;i++) {
//从扑克中心获取一张牌
Poker poker = pokerManager.getOnePoker();
//将这张牌发给对应的人
Player player = playerManager.getPlayer(i);
player.poker = poker;
}
System.out.println(playerManager.players);
}
//下注方法
//难点二:关于all-in的部分,到底是怎么执行的?
private void startBets() {
while(liveCount > 1) {
if(currentPlayerIndex == allInPosition) {
//直接比大小
break;
}
Player player = playerManager.getPlayer(currentPlayerIndex - 1);
//判断当前这个人是否已经弃牌(根据上一轮的选择,判断这一轮他是不是弃牌了)
if(player.playerState == Constants.IPlayerState.DISCARD) {
//这个人已经弃牌,下面不需要做
changeCurrentIndex();
continue;
}
if(player.chip <= lastPlayerBet || isAllIn) {
//显示操作列表
//难点三:show方法是怎么执行的
Util.show(true,true,Constants.IBet.LESS);
//提示信息
Util.show("请"+player.id+"号玩家选择操作("+player.name+" $"+player.chip+"):");
//接收用户的输入
int choice = input(Constants.IBet.LESS.length,1);//难点四:input的封装
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.chip+"):");
//接收用户的输入
int choice = input(Constants.IBet.NORMAL.length,1);
switch(choice) {
case 1:
//下注
bet(player);
break;
case 2:
//跟注
follow(player);
break;
case 3:
allIn(player);
break;
case 4:
break;
case 5:
//弃牌
giveUp(player);
break;
}
}
//当前玩家索引指向下一个
changeCurrentIndex();
}
//游戏结束
playerManager.awardPlayer(ante,smallestAllinBet);//难点四,awardPlayer方法如何执行的
System.out.println(playerManager.players);
}
//all-in
//当一个人选择all-in时,只比较一次,最大的赢,然后结束
private void allIn(Player player) {
if(isAllIn) {
//之前已经有人AllIn过了,所以要比较当前allin值
if(player.chip < smallestAllinBet) {
smallestAllinBet = player.chip;
}
}else {
//这人第一次allin
isAllIn = true;
//记录当前allin最小值
smallestAllinBet = player.chip;
//记录当前allin起始点
allInPosition = currentPlayerIndex;
}
//当前这个人all-in
player.currentBet = player.chip;
//总金额
ante += player.chip;
//下注所有
player.lostMoney(player.chip);
}
//跟注
private void follow(Player player) {
//总金额增加
ante += lastPlayerBet;
//扣除该玩家筹码
player.lostMoney(lastPlayerBet);
}
//弃牌
private void giveUp(Player player) {
System.out.println("您已弃牌");
//弃牌
player.playerState = Constants.IPlayerState.DISCARD;
//活的人少一个
liveCount--;
}
//下注方法
private void bet(Player player) {
Util.show("请输入下注金额");
int total = input(player.chip,lastPlayerBet);
//总金额增加
ante += total;
//扣除该玩家筹码
player.lostMoney(total);
//记录这次下注金额
lastPlayerBet = total;//lastPlayerBet是在这里记录的
}
//接收用户输入
private int input(int max,int min) {
Scanner scanner = new Scanner(System.in);
while(1 > 0) {
int num = scanner.nextInt();
if(num >= min && num <= max) {
return num;
}
Util.show("数据不正确,请重新输入");
}
}
//当前玩家索引值指向下一个
private void changeCurrentIndex() {
currentPlayerIndex++;
if(currentPlayerIndex > totalPlayer) {
currentPlayerIndex = 1;
}
}
@Override
public void onInitFailure() {
// TODO 自动生成的方法存根
}
}
该类难点最多,不过莫慌
难点①:setTotalSuccess(getTotalSuccess() + 1);这个计数器是干嘛的?
其实这个方法在AGameCenter里面,这里我单独把它拿出来,大家看一下,
public void onInitSuccess() {
//对成功的计数器加一
setTotalSuccess(getTotalSuccess() + 1);
}
//totalSuccess的set和get方法
public void setTotalSuccess(int totalSuccess) {
this.totalSuccess = totalSuccess;
if(this.totalSuccess == 3) {
start();//totalSuccess会被加三次,因为有三次初始化
}
}
public int getTotalSuccess() {
return this.totalSuccess;
}
其实之前在PlayerManager和PokerManager两个类里面都有listener.onInitSuccess()方法。在游戏开始之前,有三次初始化,分别是PlayerManager类、PokerManager类和PokerGameCenter类,这三个类若有一个初始化成功,那么onInitSuccess方法就会被执行一次,然后totalSuccess就会+1,等加到三的时候,说明一共有三次初始化成功,也就是达到了游戏开始的条件,那么就会执行start(),使游戏开始。看一下setTotalSuccess方法的代码,大家就可以验证我刚才说的这些了。
难点②:下注方法中关于all-in部分到底是如何执行的。
我们首先来熟悉一下all-in的规则。比如1号玩家选择下注,2号玩家选择all-in,那么3号玩家就只能选择all-in或者弃牌,不管选择哪一个,下一个玩家(也就是1号玩家)也是只能选择all-in或者弃牌。提示1号玩家选择all-in还是弃牌之后,游戏就可以结束了,因为又到了2号玩家,而2号玩家已经选择了all-in。也可以把第一个all-in的玩家看作新一轮游戏的起点,也是新一轮游戏的终点。
那么回到代码上来,为查阅方便,这里把all-in方法再单独拿过来讲解
private void allIn(Player player) {
if(isAllIn) {
//之前已经有人AllIn过了,所以要比较当前allin值
if(player.chip < smallestAllinBet) {
smallestAllinBet = player.chip;
}
}else {
//第一个人第一次allin
isAllIn = true;
//记录当前allin最小值
smallestAllinBet = player.chip;
//记录当前allin起始点
allInPosition = currentPlayerIndex;
}
//当前这个人all-in
player.currentBet = player.chip;
//总金额
ante += player.chip;
//下注所有
player.lostMoney(player.chip);
}
这里我描述一下执行过程:一上来先把这个all-in的玩家传递过来,先判断之前是不是有人all-in过了,如果有,那么拿这个人总共的筹码(也就是all-in的钱)和之前all-in的值进行比较,哪个小,就把smallestAllinBet赋值成哪一个。然后执行后面的。如果这个人是所有玩家里面第一位all-in的,那么先设置isAllIn为true,再把当前玩家的筹码赋值给smallestAllinBet,然后,也是最重要的一点,就是把这个玩家的序号记作allin的起点。(比如2号玩家第一次all-in,那么记录allInPosition为2,等又轮到2号玩家的时候,就直接进行比较然后游戏结束。
注意:当currentPlayerIndex为2的时候,指的是2号玩家,索引值为1,也就是get(1)才能得到2号玩家。
难点③:show方法的执行。
这里我单独拿出show方法来给大家看一下
//输出语句
public static void show(boolean nextLine,boolean needNumber,String...args) {
StringBuilder builder = new StringBuilder();
if(needNumber) {//是否需要序号,比如1.自由下注的“1.”是否需要
System.out.println("----------");
for(int i = 0;i < args.length;i++) {
String content = (i+1)+"."+args[i]+" ";
builder.append(content);
}
builder.append("\n----------");
}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) {
show(false,false,new String[] {content});
}
如果是执行的needNumber里面的,那么显示结果就是类似
1.下注 2.跟注 3.all-in 4.比牌 5.弃牌
反之就输出类似
下注 跟注 all-in 比牌 弃牌 这样的结果。相信大家看到结果就知道show怎么执行的了,我就不再多说。
难点④:awardPlayer方法执行
这也是上一个类的难点,当时我还没看懂,但是反复看过多遍之后终于理解了。这里再把awardPlayer方法单独拿出来讲解一下
public void awardPlayer(int money,int smallestAllinBet) {
Player max = null;
for(Player player:players) {
if(player.playerState == Constants.IPlayerState.HAND) {
if(max == null) {
//找到第一个手上有牌的人/没有弃牌的人
max = player;
}else {
int result = max.poker.compareTo(player.poker);
if(result == -1) {
//max对应的牌比player的牌要小
//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.playerState == Constants.IPlayerState.HAND) {
player.returnMoney(player.currentBet-smallestAllinBet);
totalReturn += (player.currentBet - smallestAllinBet);
}
}
//从max中退回多余的钱
max.lostMoney(totalReturn);
}
这里的money是桌面上的总筹码ante,smallestAllinBet是all-in的最小值。比如有两个玩家all-in了,第一个all-in的40,第二个all-in了50,那么肯定要取少的,也就是40,然后还要把第二个玩家多all-in的10返还给第二个玩家。max保存的是牌最大的玩家。当利用比牌方法找到max玩家之后,要把桌面上的所有筹码(ante)赏给max玩家(记住,如果有人all-in,那么这时max玩家其实是多拿了,因为还没有取all-in最小值,现在还是把所有玩家的all-in都给max了)如果没有人all-in,那直接比赛结束。如果有人all-in了,还要一个一个地返回差值,并记录返回的所有差值的和,再从max玩家里面扣所有差值的和,也就是这里的totalReturn(刚刚说了如果有人all-in那么max玩家相当于多拿了所有差值的和)。这就是这个方法的执行过程。
7.IGameInitListener
这个类是接口,就是用来被实现的,发挥着监听初始化是否成功的作用
package game;
//监听初始化是否成功
public interface IGameInitListener {
void onInitSuccess();
void onInitFailure();
}
调用它的方法就是调用实现类的相应的重写之后的方法,所以下面来讲一下它的实现类。
8.AGameCenter
package game;
import Poker1.PokerManager;
import player.PlayerManager;
public abstract class AGameCenter implements IGameInitListener{
protected PlayerManager playerManager;
protected PokerManager pokerManager;
protected int ante;//台面的总金额
protected int totalPlayer;//玩家人数
protected int bottom;//底注
protected static AGameCenter instance;
private int totalSuccess;
protected AGameCenter() {
//初始化游戏本身的数据
initGame();
//初始化玩家
initPlayers();
//初始化扑克
initPokers();
}
//下面这四个方法都在上面的PokerGameCenter类继承之后重写了。调用的时候就执行子类的重写之后的方法
protected abstract void initGame();
protected abstract void initPokers();
protected abstract void initPlayers();
protected abstract void start();
public void onInitSuccess() {
//对成功的计数器加一
setTotalSuccess(getTotalSuccess() + 1);
}
@Override
public void onInitFailure() {
}
//totalSuccess的set和get方法
public void setTotalSuccess(int totalSuccess) {
this.totalSuccess = totalSuccess;
if(this.totalSuccess == 3) {
start();//totalSuccess会被加三次,因为有三次初始化
}
}
public int getTotalSuccess() {
return this.totalSuccess;
}
}
介绍完PokerGameCenter之后,这个类就没啥可说的了。
还有工具类,工具类也已经分块介绍完了,这里再拿过来总体看一下
9.Util
package game;
import java.util.Random;
public class Util {
//生成名字
public static String autoGenerateName() {
Random random = new Random();
//姓名的随机数
int f_index = Math.abs(random.nextInt()%Constants.IPlayerName.NAMES_XING.length);
int m_index = Math.abs(random.nextInt()%Constants.IPlayerName.NAMES_MING_M.length);
int l_index = Math.abs(random.nextInt()%Constants.IPlayerName.NAMES_MING_L.length);
String f = Constants.IPlayerName.NAMES_XING[f_index];
String m = Constants.IPlayerName.NAMES_MING_M[m_index];
String l = Constants.IPlayerName.NAMES_MING_L[l_index];
return f+m+l;
}
//输出语句
public static void show(boolean nextLine,boolean needNumber,String...args) {
StringBuilder builder = new StringBuilder();
if(needNumber) {
System.out.println("----------");
for(int i = 0;i < args.length;i++) {
String content = (i+1)+"."+args[i]+" ";
builder.append(content);
}
builder.append("\n----------");
}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) {
show(false,false,new String[] {content});
}
public static int indexOfObject(String object, String ...array){
for (int i = 0; i < array.length; i++){
if (array[i].equals(object)){
return i;
}
}
return -1;
}
}
最后就是main方法
10.Main
package Poker1;
import game.PokerGameCenter;
public class Main {
public static void main(String[] args) {
PokerGameCenter center = PokerGameCenter.getInstance();
}
}
三.梳理
这里简单地说一下程序的执行顺序。
首先在main方法中得到PokerGameCenter的对象,在new PokerGameCenter()的时候会调用父类的无参构造
而父类的无参构造就是
三个都初始化完了之后,totalSuccess就等于3,然后就自动调用start()方法,然后游戏就开始了。
四.总结
本来一直都有打算研究一下这个程序的,因为很多地方地方我还没有搞清楚,不过因为懒惰拖延耽搁了。经过五六个小时的研究,终于写成了这篇博客,对于我来说收获是非常大的,因为自己之前也写过一个这样的,不过功能也不全面还有很多bug,老师讲的时候我也没有完全听懂,经过写这篇博客,我终于明白了这个项目的执行过程。同时也希望这篇文章能对大家有所帮助!加油!!!