之前說(shuō)好的一定要研究透徹?fù)淇伺祈?xiàng)目诵盼,小弟說(shuō)到做到惠豺。在下面我將把每一段代碼的功能都盡可能描述清楚,一方面是幫助我理解這個(gè)項(xiàng)目的執(zhí)行過(guò)程风宁,另一方面也是希望能對(duì)大家有所幫助洁墙。
一.總
AGameCenter是抽象類并實(shí)現(xiàn)IGameInitListener接口,主要是用來(lái)“被繼承”
Constants接口里面保存一些常量戒财,比如玩家姓名热监,撲克牌花色等等
IGameInitListener是接口,用來(lái)監(jiān)聽初始化是否成功饮寞。
PokerGameCenter繼承AGameCenter孝扛,主要負(fù)責(zé)控制整個(gè)游戲的進(jìn)行
Util是工具類
Player是玩家類列吼,里面主要有單個(gè)玩家的數(shù)據(jù)信息和操作
PlayerManager是玩家管理類,里面主要涉及操作控制所有玩家的操作
Poker是撲克類苦始,里面主要是涉及單個(gè)撲克的操作
PokerManager是撲克管理類寞钥,主要涉及操作控制所有撲克的操作。
Main里面是程序入口(這里放的包的不合適陌选,還望大家諒解)
二.分
為了大家能夠容易看懂理郑,我們由簡(jiǎn)到難,一個(gè)類一個(gè)類地進(jìn)行說(shuō)明講解
1.Constants接口
代碼如下
package game;
import Poker1.Poker;
import Poker1.Poker.PicType;
//常量
public interface Constants {
//下面是玩家提示說(shuō)明
interface IBet{
String[] NORMAL = new String[] {"下注","跟注","all-in","比牌","棄牌"};//正常情況
String[] LESS = new String[] {"all-in","棄牌"};
}
//默認(rèn)籌碼是1000
interface IPlayer{
int CHAPS = 1000;//籌碼
}
//玩家姓名常量
interface IPlayerName{
//姓名
String[] NAMES_XING = {"王","李","張","彭"};
String[] NAMES_MING_M = {"宏","濤","國(guó)","東","建","強(qiáng)"};
String[] NAMES_MING_L = {"高","曉","博","督","黎"};
}
//玩家狀態(tài)常量
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"};//點(diǎn)數(shù)
//里面保存了四個(gè)花色對(duì)象咨油,具體看后面說(shuō)明
Poker.PicType[] PIC_TYPES = {
Poker.PicType.SPADE,Poker.PicType.HRARTS,
Poker.PicType.CLUBS,Poker.PicType.DIAMONDS
};
}
}
這個(gè)類總體理解起來(lái)不難您炉,重點(diǎn)留意一下IPlayerName即玩家姓名常量和IPoker這兩個(gè)接口,具體什么功能后面會(huì)說(shuō)役电。
既然涉及Poker類里面的東西赚爵,那下面就介紹Poker
2.Poker類
package Poker1;
import game.Constants;
public class Poker {
public String dot;//牌的點(diǎn)數(shù)
public PicType type;//花色對(duì)象,這里花色是用了內(nèi)部類來(lái)保存的宴霸,類的名稱是PicType
//構(gòu)造方法
public Poker(String dot,PicType type) {
this.dot = dot;
this.type = type;
}
//難點(diǎn)一
public static class PicType{
public String pic;//花色
public int tag;//花色對(duì)應(yīng)的tag值
//由于是涉及花色囱晴,所以應(yīng)該是不可更改的,所以都用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;
}
}
//難點(diǎn)二
//比較兩張牌的大小
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;
}
}
這個(gè)類有兩個(gè)難點(diǎn)瓢谢,大家不要急畸写,在這里我會(huì)盡可能解釋明白
難點(diǎn)①,內(nèi)部類PicType:
由于比花色是無(wú)法直接比較的氓扛,所以我們?yōu)槊總€(gè)花色都設(shè)置了一種映射關(guān)系枯芬,即黑桃-4 紅桃-3 梅花-2 方片-1,把它們封裝在內(nèi)部類PicType中采郎,就可以實(shí)現(xiàn)花色的比較千所。
如何實(shí)現(xiàn)?
以SPADE為例蒜埋,這是PicType的一個(gè)對(duì)象淫痰,這個(gè)對(duì)象里面有兩個(gè)數(shù)據(jù)成員,一個(gè)是pic整份,一個(gè)是tag待错,其中tag就是其映射的數(shù)字4,通過(guò)SPADE.tag可以取出tag烈评,進(jìn)而可以和其他花色的tag進(jìn)行比較火俄。
難點(diǎn)②比較兩張牌的大小:
這里使用了Util類里面的一個(gè)方法讲冠,我在這里先把這個(gè)方法單獨(dú)拿出來(lái)
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;
}
這個(gè)方法有兩個(gè)參數(shù)瓜客,第一個(gè)參數(shù)String類的object其實(shí)傳的是這張牌的點(diǎn)數(shù)(從這個(gè)方法的調(diào)用可以看出),然后第二個(gè)參數(shù)array其實(shí)傳的是Constants類里面的DOTS數(shù)組,這個(gè)數(shù)組是所有點(diǎn)數(shù)谱仪,按照從小到大的順序來(lái)排列的玻熙,這一點(diǎn)尤為重要,其實(shí)也是為什么傳遞這個(gè)數(shù)組的原因芽卿。因?yàn)榕朴螒虻狞c(diǎn)數(shù)比大小不是簡(jiǎn)單的2<3,其中還涉及A和JQK這些的比較揭芍,而這些不是數(shù)字,所以無(wú)法與純數(shù)字2345678910直接比較卸例,所以就需要借用它們所在數(shù)組的索引值來(lái)比較大小称杨,而這個(gè)indexOfObject方法其實(shí)就是的到這個(gè)點(diǎn)數(shù)的索引值。
解釋到這里筷转,indexOfObject函數(shù)就可以先扔一邊了姑原,繼續(xù)看Poker類,我們用了兩個(gè)變量index_m和index_a來(lái)分別保存此牌和另外一張牌點(diǎn)數(shù)的索引值呜舒。比較兩張牌的話锭汛,肯定首先比較點(diǎn)數(shù),如果兩張牌索引值一樣袭蝗,那么就說(shuō)明它們點(diǎn)數(shù)相同唤殴,點(diǎn)數(shù)相同就要比較花色,如何比較花色呢到腥?其實(shí)前面已經(jīng)說(shuō)了朵逝,分別取出這兩張牌花色的tag值來(lái)代替花色進(jìn)行比較,如果這張牌的花色大乡范,就返回1配名,如果另外一張牌的花色大,就返回-1.
如果點(diǎn)數(shù)不相同晋辆,就更好說(shuō)了渠脉,如果這張牌的點(diǎn)數(shù)大,就返回1瓶佳,如果另外一張牌的點(diǎn)數(shù)大芋膘,就返回-1
3.PokerManager類
package Poker1;
import java.util.*;
import game.Constants;
import game.IGameInitListener;
public class PokerManager {
//監(jiān)聽器,這個(gè)東西的功能我一直很迷糊霸饲,知道前幾天才理解透徹索赏,在這里與大家分享
private IGameInitListener listener;
private ArrayList<Poker> pokers;//保存撲克牌
//創(chuàng)建單例
private static PokerManager manager;//注意這里要是靜態(tài)
private PokerManager() {
}
//獲得PokerManager對(duì)象的方法,注意這里需要加鎖贴彼,這里一定要會(huì)寫,屬于基本功埃儿。
public static PokerManager getManager() {
if(manager == null) {
synchronized(PokerManager.class) {
if(manager == null) {
manager = new PokerManager();
}
}
}
return manager;
}
//初始化所有撲克牌
public void initPokers() {
//初始化數(shù)組
pokers = new ArrayList<>();//這一步是經(jīng)常忘掉的器仗,一定要對(duì)數(shù)組進(jìn)行初始化
//創(chuàng)建撲克牌
for(String dot:Constants.IPoker.DOTS) {
//難點(diǎn)一:type對(duì)象是Poker.PicType類的一個(gè)對(duì)象,這個(gè)對(duì)象從Constants.IPoker.PIC_TYPES數(shù)組里面去取
for(Poker.PicType type: Constants.IPoker.PIC_TYPES) {
//創(chuàng)建一張牌
Poker poker = new Poker(dot,type);
//添加到數(shù)組中保存
pokers.add(poker);
}
}
//打亂順序
Collections.shuffle(pokers);
//輸出牌
//System.out.println(pokers);
//當(dāng)撲克牌初始化成功,就回調(diào)成功的事件給listener
if(listener != null) {//難點(diǎn)二:為什么這里listener不是null了呢精钮?后面會(huì)給大家做詳細(xì)解釋的威鹿。
//成功的計(jì)數(shù)加1
listener.onInitSuccess();
}
}
//獲取一張牌,然后從數(shù)組里面將這張牌刪掉
//難點(diǎn)三:這里為什么要將這張牌刪掉轨香?后面會(huì)有解釋忽你。
public Poker getOnePoker() {
if(pokers.size() > 0) {
//獲取第一張牌
Poker poker = pokers.get(0);
//將這張牌從數(shù)組里面刪除
pokers.remove(0);
return poker;
}
return null;
}
//難點(diǎn)四:這個(gè)東西是干嘛的呢?
public void setListener(IGameInitListener listener) {
this.listener = listener;
}
}
這個(gè)類難點(diǎn)相對(duì)多一點(diǎn)臂容,不過(guò)大家也不用慌科雳,我還是會(huì)一一地詳細(xì)解釋的
難點(diǎn)①:創(chuàng)建撲克牌。
其實(shí)這里的操作也不是很難理解:利用兩個(gè)for循環(huán)脓杉,先確定點(diǎn)數(shù)糟秘,然后分別給每個(gè)點(diǎn)數(shù)附上花色。但是稍微有點(diǎn)難度的是這里的增強(qiáng)for循環(huán)和PIC_TYPES數(shù)組球散。這個(gè)數(shù)組里面存了四個(gè)元素尿赚,這四個(gè)元素都是花色對(duì)象,而花色對(duì)象的類型是Poker.PicType類型蕉堰,所以代碼就出來(lái)了凌净。
難點(diǎn)②:為何listener不是空的呢?我也沒(méi)看見對(duì)它進(jìn)行賦值呀屋讶?
關(guān)于這一點(diǎn)冰寻,得需要后面的類介紹完了才能解釋。
難點(diǎn)③:為什么取出這張牌了之后要把這張牌刪除呢丑婿?
就跟實(shí)際玩牌一樣性雄,一個(gè)玩家取出來(lái)了一張牌,那么牌堆兒里面就沒(méi)有那張牌了羹奉。很好理解
難點(diǎn)④:setListener函數(shù)是用來(lái)干嘛的秒旋?
和難點(diǎn)②一樣,后面都會(huì)有解釋
4.Player
package player;
import java.util.*;
import Poker1.Poker;
import game.Constants;
public class Player {
public int id;//編號(hào)
public String name;//名字
public int chip;//籌碼
public Poker poker;//玩家手上的一張牌
public ArrayList<Poker> pokers;//多張牌
public int playerState;//游戲狀態(tài)
public int currentBet;//當(dāng)前下注金額
//構(gòu)造方法
public Player(int id,String name,int chip) {
this.id = id;
this.name = name;
this.chip = chip;
//初始化狀態(tài)
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;
}
}
這個(gè)類無(wú)難點(diǎn)诀拭,繼續(xù)往下說(shuō)
5.PlayerManager
package player;
import java.util.*;
import game.*;
public class PlayerManager {
private IGameInitListener listener;
public ArrayList<Player> players;
//創(chuàng)建單例
private static PlayerManager manager;//注意這里要是靜態(tài)
private PlayerManager() {
}
public static PlayerManager getManager() {
if(manager == null) {
synchronized(PlayerManager.class) {
if(manager == null) {
manager = new PlayerManager();
}
}
}
return manager;
}
//初始化所有玩家
public void initPlayers(int totalPlayer) {
//創(chuàng)建玩家數(shù)組
players = new ArrayList<>();
for(int i = 0;i < totalPlayer;i++) {
//獲取玩家名字(難點(diǎn)一:如何獲取的玩家名字)
String name = Util.autoGenerateName();
//創(chuàng)建玩家對(duì)象
Player player = new Player(i+1,name,Constants.IPlayer.CHAPS);
//保存玩家
players.add(player);
}
//當(dāng)玩家初始化成功迁筛,就回調(diào)成功的事件給監(jiān)聽者/listener/游戲中心(難點(diǎn)二:listener為何是null?)
if(listener != null) {
listener.onInitSuccess();
}
}
//獲取玩家人數(shù)
public int getPlayerCount() {
return players.size();
}
//扣底注錢的方法(打底)耕挨,所有玩家都扣
public void deDuctMoney(int count) {
for(Player player:players) {
player.lostMoney(count);
}
}
//獲取一個(gè)玩家
public Player getPlayer(int index) {
return players.get(index);
}
//難點(diǎn)二:money细卧,smallestAllinBet是什么意思,整個(gè)方法的邏輯是什么筒占?
public void awardPlayer(int money,int smallestAllinBet) {
Player max = null;
for(Player player:players) {
if(player.playerState == Constants.IPlayerState.HAND) {
if(max == null) {
//找到第一個(gè)手上有牌的人/沒(méi)有棄牌的人
max = player;
}else {
int result = max.poker.compareTo(player.poker);
if(result == -1) {
//max對(duì)應(yīng)的牌比player的牌要小
//max記錄最大的那個(gè)玩家
max = player;
}
}
}
}
//最大的人贏錢
max.winMoney(money);
//沒(méi)有人all-in
if(smallestAllinBet == 0) {
return;
}
//將max之外的所有all-in的人多于的錢返還
int totalReturn = 0;
for(Player player:players) {
//找到?jīng)]有棄牌 并且 不是當(dāng)前最大的那個(gè)人
if(!player.equals(max) && player.playerState == Constants.IPlayerState.HAND) {
player.returnMoney(player.currentBet-smallestAllinBet);//此人當(dāng)前下注金額減去最小的all-in值就是需要返還的金額
totalReturn += (player.currentBet - smallestAllinBet);
}
}
//從max中退回多余的錢(max多拿了totalReturn)
max.lostMoney(totalReturn);
}
//難點(diǎn)三:這個(gè)方法的意義贪庙?
public void setListener(IGameInitListener listener) {
this.listener = listener;
}
}
難點(diǎn)①:獲取玩家姓名方法如何執(zhí)行的。
首先來(lái)看Util類中獲取姓名的方法
public static String autoGenerateName() {
Random random = new Random();
//姓名的隨機(jī)數(shù)
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的作用是想要獲取數(shù)組所有索引值之一翰苫,但是有負(fù)數(shù)止邮。Math.abs(x) 函數(shù)返回指定數(shù)字 “x“ 的絕對(duì)值这橙。,也就是獲得了真真正正的索引值导披,即0~數(shù)組最后一個(gè)元素的索引值屈扎。然后再將隨機(jī)產(chǎn)生的姓,中間的名撩匕,和末尾的名鹰晨,拼接起來(lái)(假設(shè)名字有三個(gè)漢字),就生成了名字止毕。
難點(diǎn)②:public void awardPlayer(int money,int smallestAllinBet)這個(gè)方法是用來(lái)干啥的模蜡?
后面會(huì)有相應(yīng)解釋
難點(diǎn)③: public void setListener(IGameInitListener listener) 有什么作用?
和PokerManager一樣的難點(diǎn)滓技,后面都有相應(yīng)解釋哩牍,請(qǐng)大家耐心閱讀。
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;//生存的玩家數(shù)目
private int currentPlayerIndex;//當(dāng)前玩家索引值
private int lastPlayerBet;//上一個(gè)玩家下注的金額
private boolean isAllIn = false;//是否all-in
private int allInPosition;//從哪開始all-in的
private int smallestAllinBet;
//創(chuàng)建單例
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() {
//創(chuàng)建對(duì)象
playerManager = PlayerManager.getManager();
pokerManager = PokerManager.getManager();
ante = 0;//臺(tái)面的總金額
totalPlayer = 3;//三個(gè)玩家
bottom = 5;//底注
liveCount = totalPlayer;
currentPlayerIndex = 1;
//設(shè)置監(jiān)聽者
playerManager.setListener(this);//這里就知道為什么listener不是null了
pokerManager.setListener(this);
//初始化完畢令漂,成功的計(jì)數(shù)器+1
setTotalSuccess(getTotalSuccess() + 1);//難點(diǎn)一:這個(gè)計(jì)數(shù)器是干嘛的膝昆?
}
@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.發(fā)牌
dealCards();
//3.開始下注
startBets();
}
//發(fā)牌方法
private void dealCards() {
int count = playerManager.getPlayerCount();
for(int i = 0;i < count;i++) {
//從撲克中心獲取一張牌
Poker poker = pokerManager.getOnePoker();
//將這張牌發(fā)給對(duì)應(yīng)的人
Player player = playerManager.getPlayer(i);
player.poker = poker;
}
System.out.println(playerManager.players);
}
//下注方法
//難點(diǎn)二:關(guān)于all-in的部分,到底是怎么執(zhí)行的叠必?
private void startBets() {
while(liveCount > 1) {
if(currentPlayerIndex == allInPosition) {
//直接比大小
break;
}
Player player = playerManager.getPlayer(currentPlayerIndex - 1);
//判斷當(dāng)前這個(gè)人是否已經(jīng)棄牌(根據(jù)上一輪的選擇荚孵,判斷這一輪他是不是棄牌了)
if(player.playerState == Constants.IPlayerState.DISCARD) {
//這個(gè)人已經(jīng)棄牌,下面不需要做
changeCurrentIndex();
continue;
}
if(player.chip <= lastPlayerBet || isAllIn) {
//顯示操作列表
//難點(diǎn)三:show方法是怎么執(zhí)行的
Util.show(true,true,Constants.IBet.LESS);
//提示信息
Util.show("請(qǐng)"+player.id+"號(hào)玩家選擇操作("+player.name+" $"+player.chip+"):");
//接收用戶的輸入
int choice = input(Constants.IBet.LESS.length,1);//難點(diǎn)四: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("請(qǐng)"+player.id+"號(hào)玩家選擇操作("+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;
}
}
//當(dāng)前玩家索引指向下一個(gè)
changeCurrentIndex();
}
//游戲結(jié)束
playerManager.awardPlayer(ante,smallestAllinBet);//難點(diǎn)四纬朝,awardPlayer方法如何執(zhí)行的
System.out.println(playerManager.players);
}
//all-in
//當(dāng)一個(gè)人選擇all-in時(shí)收叶,只比較一次,最大的贏共苛,然后結(jié)束
private void allIn(Player player) {
if(isAllIn) {
//之前已經(jīng)有人AllIn過(guò)了判没,所以要比較當(dāng)前allin值
if(player.chip < smallestAllinBet) {
smallestAllinBet = player.chip;
}
}else {
//這人第一次allin
isAllIn = true;
//記錄當(dāng)前allin最小值
smallestAllinBet = player.chip;
//記錄當(dāng)前allin起始點(diǎn)
allInPosition = currentPlayerIndex;
}
//當(dāng)前這個(gè)人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;
//活的人少一個(gè)
liveCount--;
}
//下注方法
private void bet(Player player) {
Util.show("請(qǐng)輸入下注金額");
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("數(shù)據(jù)不正確,請(qǐng)重新輸入");
}
}
//當(dāng)前玩家索引值指向下一個(gè)
private void changeCurrentIndex() {
currentPlayerIndex++;
if(currentPlayerIndex > totalPlayer) {
currentPlayerIndex = 1;
}
}
@Override
public void onInitFailure() {
// TODO 自動(dòng)生成的方法存根
}
}
該類難點(diǎn)最多隅茎,不過(guò)莫慌
難點(diǎn)①:setTotalSuccess(getTotalSuccess() + 1);這個(gè)計(jì)數(shù)器是干嘛的澄峰?
其實(shí)這個(gè)方法在AGameCenter里面,這里我單獨(dú)把它拿出來(lái)辟犀,大家看一下俏竞,
public void onInitSuccess() {
//對(duì)成功的計(jì)數(shù)器加一
setTotalSuccess(getTotalSuccess() + 1);
}
//totalSuccess的set和get方法
public void setTotalSuccess(int totalSuccess) {
this.totalSuccess = totalSuccess;
if(this.totalSuccess == 3) {
start();//totalSuccess會(huì)被加三次,因?yàn)橛腥纬跏蓟? }
}
public int getTotalSuccess() {
return this.totalSuccess;
}
其實(shí)之前在PlayerManager和PokerManager兩個(gè)類里面都有listener.onInitSuccess()方法堂竟。在游戲開始之前魂毁,有三次初始化,分別是PlayerManager類出嘹、PokerManager類和PokerGameCenter類席楚,這三個(gè)類若有一個(gè)初始化成功,那么onInitSuccess方法就會(huì)被執(zhí)行一次税稼,然后totalSuccess就會(huì)+1烦秩,等加到三的時(shí)候刁赦,說(shuō)明一共有三次初始化成功,也就是達(dá)到了游戲開始的條件闻镶,那么就會(huì)執(zhí)行start(),使游戲開始丸升∶看一下setTotalSuccess方法的代碼,大家就可以驗(yàn)證我剛才說(shuō)的這些了狡耻。
難點(diǎn)②:下注方法中關(guān)于all-in部分到底是如何執(zhí)行的墩剖。
我們首先來(lái)熟悉一下all-in的規(guī)則。比如1號(hào)玩家選擇下注夷狰,2號(hào)玩家選擇all-in岭皂,那么3號(hào)玩家就只能選擇all-in或者棄牌,不管選擇哪一個(gè)沼头,下一個(gè)玩家(也就是1號(hào)玩家)也是只能選擇all-in或者棄牌爷绘。提示1號(hào)玩家選擇all-in還是棄牌之后,游戲就可以結(jié)束了进倍,因?yàn)橛值搅?號(hào)玩家土至,而2號(hào)玩家已經(jīng)選擇了all-in。也可以把第一個(gè)all-in的玩家看作新一輪游戲的起點(diǎn)猾昆,也是新一輪游戲的終點(diǎn)陶因。
那么回到代碼上來(lái),為查閱方便垂蜗,這里把a(bǔ)ll-in方法再單獨(dú)拿過(guò)來(lái)講解
private void allIn(Player player) {
if(isAllIn) {
//之前已經(jīng)有人AllIn過(guò)了楷扬,所以要比較當(dāng)前allin值
if(player.chip < smallestAllinBet) {
smallestAllinBet = player.chip;
}
}else {
//第一個(gè)人第一次allin
isAllIn = true;
//記錄當(dāng)前allin最小值
smallestAllinBet = player.chip;
//記錄當(dāng)前allin起始點(diǎn)
allInPosition = currentPlayerIndex;
}
//當(dāng)前這個(gè)人all-in
player.currentBet = player.chip;
//總金額
ante += player.chip;
//下注所有
player.lostMoney(player.chip);
}
這里我描述一下執(zhí)行過(guò)程:一上來(lái)先把這個(gè)all-in的玩家傳遞過(guò)來(lái),先判斷之前是不是有人all-in過(guò)了贴见,如果有烘苹,那么拿這個(gè)人總共的籌碼(也就是all-in的錢)和之前all-in的值進(jìn)行比較,哪個(gè)小蝇刀,就把smallestAllinBet賦值成哪一個(gè)螟加。然后執(zhí)行后面的。如果這個(gè)人是所有玩家里面第一位all-in的吞琐,那么先設(shè)置isAllIn為true捆探,再把當(dāng)前玩家的籌碼賦值給smallestAllinBet,然后站粟,也是最重要的一點(diǎn)黍图,就是把這個(gè)玩家的序號(hào)記作allin的起點(diǎn)。(比如2號(hào)玩家第一次all-in奴烙,那么記錄allInPosition為2助被,等又輪到2號(hào)玩家的時(shí)候剖张,就直接進(jìn)行比較然后游戲結(jié)束。
注意:當(dāng)currentPlayerIndex為2的時(shí)候揩环,指的是2號(hào)玩家搔弄,索引值為1,也就是get(1)才能得到2號(hào)玩家丰滑。
難點(diǎn)③:show方法的執(zhí)行顾犹。
這里我單獨(dú)拿出show方法來(lái)給大家看一下
//輸出語(yǔ)句
public static void show(boolean nextLine,boolean needNumber,String...args) {
StringBuilder builder = new StringBuilder();
if(needNumber) {//是否需要序號(hào),比如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());
}
}
//輸出一行褒墨,不換行炫刷,不需要編號(hào)的數(shù)據(jù)
public static void show(String content) {
show(false,false,new String[] {content});
}
如果是執(zhí)行的needNumber里面的,那么顯示結(jié)果就是類似
1.下注 2.跟注 3.all-in 4.比牌 5.棄牌
反之就輸出類似
下注 跟注 all-in 比牌 棄牌 這樣的結(jié)果郁妈。相信大家看到結(jié)果就知道show怎么執(zhí)行的了浑玛,我就不再多說(shuō)。
難點(diǎn)④:awardPlayer方法執(zhí)行
這也是上一個(gè)類的難點(diǎn)噩咪,當(dāng)時(shí)我還沒(méi)看懂顾彰,但是反復(fù)看過(guò)多遍之后終于理解了。這里再把a(bǔ)wardPlayer方法單獨(dú)拿出來(lái)講解一下
public void awardPlayer(int money,int smallestAllinBet) {
Player max = null;
for(Player player:players) {
if(player.playerState == Constants.IPlayerState.HAND) {
if(max == null) {
//找到第一個(gè)手上有牌的人/沒(méi)有棄牌的人
max = player;
}else {
int result = max.poker.compareTo(player.poker);
if(result == -1) {
//max對(duì)應(yīng)的牌比player的牌要小
//max記錄最大的那個(gè)玩家
max = player;
}
}
}
}
//最大的人贏錢
max.winMoney(money);
//沒(méi)有人all-in
if(smallestAllinBet == 0) {
return;
}
//將max之外的所有all-in的人多于的錢返還
int totalReturn = 0;
for(Player player:players) {
//找到?jīng)]有棄牌 并且 不是當(dāng)前最大的那個(gè)人
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的最小值拘央。比如有兩個(gè)玩家all-in了,第一個(gè)all-in的40书在,第二個(gè)all-in了50灰伟,那么肯定要取少的,也就是40儒旬,然后還要把第二個(gè)玩家多all-in的10返還給第二個(gè)玩家栏账。max保存的是牌最大的玩家。當(dāng)利用比牌方法找到max玩家之后栈源,要把桌面上的所有籌碼(ante)賞給max玩家(記住挡爵,如果有人all-in,那么這時(shí)max玩家其實(shí)是多拿了甚垦,因?yàn)檫€沒(méi)有取all-in最小值茶鹃,現(xiàn)在還是把所有玩家的all-in都給max了)如果沒(méi)有人all-in,那直接比賽結(jié)束艰亮。如果有人all-in了闭翩,還要一個(gè)一個(gè)地返回差值,并記錄返回的所有差值的和迄埃,再?gòu)膍ax玩家里面扣所有差值的和疗韵,也就是這里的totalReturn(剛剛說(shuō)了如果有人all-in那么max玩家相當(dāng)于多拿了所有差值的和)。這就是這個(gè)方法的執(zhí)行過(guò)程侄非。
7.IGameInitListener
這個(gè)類是接口蕉汪,就是用來(lái)被實(shí)現(xiàn)的流译,發(fā)揮著監(jiān)聽初始化是否成功的作用
package game;
//監(jiān)聽初始化是否成功
public interface IGameInitListener {
void onInitSuccess();
void onInitFailure();
}
調(diào)用它的方法就是調(diào)用實(shí)現(xiàn)類的相應(yīng)的重寫之后的方法,所以下面來(lái)講一下它的實(shí)現(xiàn)類者疤。
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;//臺(tái)面的總金額
protected int totalPlayer;//玩家人數(shù)
protected int bottom;//底注
protected static AGameCenter instance;
private int totalSuccess;
protected AGameCenter() {
//初始化游戲本身的數(shù)據(jù)
initGame();
//初始化玩家
initPlayers();
//初始化撲克
initPokers();
}
//下面這四個(gè)方法都在上面的PokerGameCenter類繼承之后重寫了福澡。調(diào)用的時(shí)候就執(zhí)行子類的重寫之后的方法
protected abstract void initGame();
protected abstract void initPokers();
protected abstract void initPlayers();
protected abstract void start();
public void onInitSuccess() {
//對(duì)成功的計(jì)數(shù)器加一
setTotalSuccess(getTotalSuccess() + 1);
}
@Override
public void onInitFailure() {
}
//totalSuccess的set和get方法
public void setTotalSuccess(int totalSuccess) {
this.totalSuccess = totalSuccess;
if(this.totalSuccess == 3) {
start();//totalSuccess會(huì)被加三次,因?yàn)橛腥纬跏蓟? }
}
public int getTotalSuccess() {
return this.totalSuccess;
}
}
介紹完P(guān)okerGameCenter之后驹马,這個(gè)類就沒(méi)啥可說(shuō)的了竞漾。
還有工具類,工具類也已經(jīng)分塊介紹完了窥翩,這里再拿過(guò)來(lái)總體看一下
9.Util
package game;
import java.util.Random;
public class Util {
//生成名字
public static String autoGenerateName() {
Random random = new Random();
//姓名的隨機(jī)數(shù)
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;
}
//輸出語(yǔ)句
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());
}
}
//輸出一行,不換行鳞仙,不需要編號(hào)的數(shù)據(jù)
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();
}
}
三.梳理
這里簡(jiǎn)單地說(shuō)一下程序的執(zhí)行順序寇蚊。
首先在main方法中得到PokerGameCenter的對(duì)象,在new PokerGameCenter()的時(shí)候會(huì)調(diào)用父類的無(wú)參構(gòu)造
而父類的無(wú)參構(gòu)造就是
三個(gè)都初始化完了之后棍好,totalSuccess就等于3仗岸,然后就自動(dòng)調(diào)用start()方法,然后游戲就開始了借笙。
四.總結(jié)
本來(lái)一直都有打算研究一下這個(gè)程序的扒怖,因?yàn)楹芏嗟胤降胤轿疫€沒(méi)有搞清楚,不過(guò)因?yàn)閼卸柰涎拥R了业稼。經(jīng)過(guò)五六個(gè)小時(shí)的研究盗痒,終于寫成了這篇博客,對(duì)于我來(lái)說(shuō)收獲是非常大的低散,因?yàn)樽约褐耙矊戇^(guò)一個(gè)這樣的俯邓,不過(guò)功能也不全面還有很多bug,老師講的時(shí)候我也沒(méi)有完全聽懂熔号,經(jīng)過(guò)寫這篇博客稽鞭,我終于明白了這個(gè)項(xiàng)目的執(zhí)行過(guò)程。同時(shí)也希望這篇文章能對(duì)大家有所幫助引镊!加油k獭!弟头!