该游戏为书中贯穿前八章,一个非常全面的游戏实现,
其中涉及到,数组,集合,集合工具集,泛型,异常等
还是比较全面,其中的逻辑也比较复杂,主要是因为不知道游戏规则,导致业务逻辑无法继续,
该代码实现了几乎梭哈游戏的所有内容,只有牌型比较没有实现,如果实现,则代码性质将变质,我的学习之路到此为止,
以下
package java相关小实验;
import org.apache.commons.lang3.ArrayUtils;
import java.util.*;
/**
* 一些声明信息
*
* @author 86150
* @date: 2022/7/2 10:12
* @description: 梭哈游戏
* @since JDK 1.17
* <p>
* 关于梭哈游戏的规则
* 我自己也没玩过,网上解释并不能解释我的疑惑,好在本程序只是一个实验性质的学习项目,也不用太过较真,
* 为了使程序运行,我将在规则中加入自己的理解,我会打上标识
* <p>
* 游戏开始后,先发给各家2张牌,从第二张牌开始自动亮出,
* <my>游戏开始时,每位玩家2000欢乐豆,入场费200欢乐豆,在具体的游戏规则有两套规则,选择方案二</my>
* -----------------------------------------------------------------------------
* 方案一:
* 每发一张牌,从牌面较大者逆时针下注。优先下注者可选择
* <my>每次下注100欢乐豆起步,每次最高下注金额为5*入场费</my>
* <p>
* 1.下注(选择欢乐豆数量,范围100<选择<5*入场费)
* 2.不加(再次对自己下注,下注欢乐豆数量>你要加注的人欢乐豆数量)
* 3.放弃
* <p>
* 在别人下注之后,可考虑
* 1.跟(选择欢乐豆数量,范围100<选择<5*入场费)
* 2.加(再次对自己下注,下注欢乐豆数量>你要加注的人欢乐豆数量)
* <my>并且只要选择“跟”或“加”,则跳过本轮下注阶段</my>
* 如果使用此方案,考虑在每次下注阶段使用Map,value值存储是否已经下注,每次判断value值,为0下注,为1跳过
* -----------------------------------------------------------------------------
* 方案二:
* <p>
* 每发一张牌,从牌面较大者逆时针下注。优先下注者可选择
* <my>每次下注100欢乐豆起步,每次最高下注金额为5*入场费</my>
* <p>
* 1.下注(选择欢乐豆数量,范围100<选择<5*入场费)
* 2.不加(再次对自己下注,下注欢乐豆数量>你要加注的人欢乐豆数量)
* 3.放弃
* 之后每个人可选择:
* 1.跟(下注和上家一样筹码)
* 2.加(下注筹码<上家筹码<5*入场费)
* 3.放弃
* -------------------------------------------------------------------------------
* <p>
* 当发到第四张牌时,可以选择“梭”,即增加下注到允许的最大筹码值
* <my>最大筹码值=5*入场费</my>
* <p>
* 最后的胜利者获得本局桌面上的全部筹码,
* <p>
* 如果输家剩余的筹码数少于规定坐下的最小数额将被请出桌子。
* <my>最小数额:欢乐豆<100</my>
* ------------------------------------------------------------------------------------
* 【牌型比较】
* 牌型:同花顺>铁支>葫芦>同花>顺子>三条>两对>对子>散牌
* 点数:A>K>Q>J>10>9>8
* 花色:黑桃>红桃>草花>方片
* ------------------------------------------------------------------------------------
* 【牌型说明】
* 同花顺:拥有五张连续数字同花色的顺子,以黑桃A为首的同花顺最大;
* 铁支:四张相同数字的牌,外加一单张。比数字大小,「A」铁支最大;
* 葫芦:由「三条」加一个「对子」所组成的牌。若别家也有此牌型,则比三条数字大小;
* 同花:不构成顺子的五张同花色的牌。比花色后比大小;
* 顺子:五张连续数字不花色的牌组成。 以A为首的顺子最大,如果大家都是顺子,比最大的一张牌,如果大小还一样就比这张牌的花色,黑桃A顺子最大;
* 三条:牌型由三张相同的牌组成,以A为首的三条最大;
* 二对:牌型中五张牌由两组两张同数字的牌所组成。若遇相同则先比这副牌中最大的一对,如又相同再比第二对,如果还是一样,比大对子中的最大花色;
* 对子:牌型由两张相同的牌加上三张单张所组成。如果大家都是对子,比对子的大小,如果对子也一样,比这个对子中的最大花色
* 散牌:由单一型态,不构成上面的牌型的五张散牌组成,先比最大一张牌的大小,如果大小一样,比这张牌的花色
*/
public class ShowHand {
//定义该游戏最多支持多少个玩家
private final int PLAY_NUM = 5;
// 定义入场费
private final int NUM = 200;
//定义扑克牌的所有花色和数值
private String[] types = {"方块-", "梅花-", "红心-", "黑桃-"};
private String[] values = {"2", "3", "4", "5"
, "6", "7", "8", "9", "10"
, "J", "Q", "K", "A"};
// 游戏开始金额
private Integer[] money = new Integer[PLAY_NUM];
// 每次的下注值
private Integer[] moneyHand = new Integer[PLAY_NUM];
// cards是一局游戏中剩下的扑克牌
private List<String> cards = new LinkedList<String>();
// 定义所有的玩家
private String[] players = new String[PLAY_NUM];
// 所有玩家手上的扑克牌
private List<String>[] playersCards = new List[PLAY_NUM];
/**
* 初始化扑克牌,放入52张扑克牌
* 并且使用shuffle方法将它们按随机顺序排列
*/
public void initCards() {
for (int i = 0; i < types.length; i++) {
for (int j = 0; j < values.length; j++) {
cards.add(types[i] + values[j]);
}
}
//随机排列
Collections.shuffle(cards);
}
/**
* 初始化玩家,为每个玩家分派用户名
*/
public void initPlayer(String... names) {
System.out.println("玩家数量:" + names.length);
try {
if (names.length > PLAY_NUM || names.length < 2) {
//初始化玩家用户名
for (int i = 0; i < names.length; i++) {
players[i] = names[i];
// 初始化金额
money[i] = 2000;
}
}
} catch (Exception e) {
//校验玩家数量,
System.out.println("玩家数量不对");
// 直接结束程序
System.exit(0);
}
//初始化玩家用户名
for (int i = 0; i < names.length; i++) {
players[i] = names[i];
}
}
/**
* 初始化玩家手上的扑克牌,开始游戏时每个玩家手上的扑克牌为空
* 程序使用一个长度为0的LinkedList来表示
*/
public void initPlayerCards() {
for (int i = 0; i < players.length; i++) {
if (players[i] != null && !"".equals(players[i])) {
// 给每个玩家一个linkedList数组,用于存储手中的牌
// 这个逻辑比较绕, 声明一个list的泛型(字符串)变量 创建一个list数组,长度为5
// 这里的泛型<String>让我看的有点迷糊,如果去掉,会出错
// private List<String>[] playersCards = new List[PLAY_NUM];
// 给这个list数组的每个位置,给一个链表,限制加入元素类型为string
playersCards[i] = new LinkedList<String>();
// 下面代码报错 java不允许创建泛型类型的数组实例
// List<String>[] playersCards = new LinkedList<String>();
// 将金额初始化位2000,加减的值为入场费
money[i] = 2000 - NUM;
moneyHand[i] = 0 + NUM;
}
}
}
/**
* 输出全部扑克牌,该方法没有实际作用,仅用作测试
*/
public void showAllCards() {
for (String card : cards) {
System.out.println(card);
}
}
/**
* 派扑克牌
*
* @paramfirst 最先派给谁
*/
public void deliverCard(String first) {
//查询出指定元素在数组中的索引
int firstPos = ArrayUtils.indexOf(players, first);
//依次给位于该指定玩家之后的每个玩家派扑克牌
for (int i = firstPos; i < PLAY_NUM; i++) {
if (players[i] != null) {
// 从牌堆(cards)中拿牌
playersCards[i].add(cards.get(0));
// 去掉已经发的牌
cards.remove(0);
}
}
//依次给位于该指定玩家之前的每个玩家派扑克牌
for (int i = 0; i < firstPos; i++) {
if (players[i] != null) {
playersCards[i].add(cards.get(0));
cards.remove(0);
}
}
}
/**
* 输出玩家手上的扑克牌
* 实现该方法时,应该控制每个玩家看不到别人的第一张牌,用count计数器实现
*/
public void showPlayerCards() {
for (int i = 0; i < PLAY_NUM; i++) {
//当该玩家不为空时
if (players[i] != null) {
//输出玩家
System.out.print(players[i] + " : \t");
// 计数器
int count = 1;
//遍历输出玩家手上的扑克牌
for (String card : playersCards[i]) {
// 跳过第一张手牌的输出查看
if (count == 1) {
System.out.print("* ");
count++;
continue;
}
System.out.print(card + "\t");
}
}
System.out.print("\n");
}
}
/**
* 展示每个人的第一张手牌
*/
public void showOnewCards() {
for (int i = 0; i < PLAY_NUM; i++) {
//当该玩家不为空时
if (players[i] != null) {
//输出玩家
System.out.print(players[i] + " ,你的第一张手牌是:\t ");
System.out.print(playersCards[i].get(0) + "\t");
}
System.out.print("\n");
}
}
/**
* @param
* @description: 比较牌面大小,下注方法
* @return: void
* @author: 谢柯
* @time: 2022/7/23 17:25
*/
public void bet() {
int t = playersCards[0].size();
System.out.println("每个玩家手中的牌数:" + t);
// 牌面比较
String a = "";
// 花色比较
String a1 = "无,牌面比较有结果";
// 记录下标
int index01 = 0;
// 获取每个玩家手上,刚发的牌
for (int i = 0; i < PLAY_NUM; i++) {
if (players[i] != null) {
// 按”-“切割,存在card中
// 这是个比较奇怪的数组,在一个list中取第i个,这个里面存的是一个linkedlist的链表,再用get(索引)取
String[] card = playersCards[i].get(t - 1).split("-");
// 牌面比较,记录大者
if ("".equals(a) || contrast(a, card[1]) == 1) {
a = card[1]; // 则记录 1.牌面
a1 = card[0]; // 2.花色
index01 = i; // 3.下标
} else
// 牌面相同 花色判断
if (contrast(a, card[1]) == 0) {
int b = contrastColour(a1, card[0]);
if (b == 1) {
//a的花色大,不变
} else if (b == -1) {
// 现有的花色大
a = card[1];
a1 = card[0];
index01 = i;
} else if (b == 0) {
//这里不改变值,就记录最开始的值
System.out.println("相同牌面,相同花色,按发牌人逆时针,顺位说话");
System.out.println("现实中不存在这种情况,出错");
System.exit(0);
}
System.out.println("上轮发牌中,花色最大值为:" + a1 );
}
}
}
System.out.println("上轮发牌中,最大值为:" + a);
System.out.println("上轮发牌中,最大值拥有者是:" + index01 + "\n");
//下注
xiazhu(index01);
}
/**
* @param a
* @param b
* @description: 比较牌面大小 返回值含义 1(a<b) -1(a>b) 0(a==b)
* @return: int
* @author: 谢柯
* @time: 2022/7/25 8:52
*/
public int contrast(String a, String b) {
Map<String, Integer> s = new HashMap<String, Integer>();
s.put("A", 1);
s.put("2", 2);
s.put("3", 3);
s.put("4", 4);
s.put("5", 5);
s.put("6", 6);
s.put("7", 7);
s.put("8", 8);
s.put("9", 9);
s.put("10", 10);
s.put("J", 11);
s.put("Q", 12);
s.put("K", 13);
// a,b必须都在map中,下面的比较才有意义
if (s.containsKey(a) && s.containsKey(b)) {
int p = s.get(a).intValue();
int q = s.get(b).intValue();
// 用键对应的值做判断,直接返回大值
if (p > q) {
return -1;
} else if (p < q) {
return 1;
} else if (p == q) {
return 0;
}
}
System.out.println("有内鬼!停止交易");
System.exit(0);
return 0;
}
/**
* @param a b
* @description: 比较花色大小 返回值意义 1(a>b) -1(a<b) 0(a==b)
* 比较规则 黑桃>红心>梅花>方块
* @return: int
* @author: 谢柯
* @time: 2022/7/25 8:50
*/
public int contrastColour(String a, String b) {
Map<String, Integer> s = new HashMap<String, Integer>();
s.put("方块", 1);
s.put("梅花", 2);
s.put("红心", 3);
s.put("黑桃", 4);
// a,b必须都在map中,下面的比较才有意义
if (s.containsKey(a) && s.containsKey(b)) {
int p = s.get(a).intValue();
int q = s.get(b).intValue();
// 用键对应的值做判断,直接返回大值
// 黑桃>红心>梅花>方块
if (p > q) {
return 1;
} else if (p < q) {
return -1;
} else if (p == q) {
return 0;
}
}
System.out.println("有内鬼!停止交易");
System.exit(0);
return 0;
}
/**
* @param firstPos
* @description: 跟据传来的下标, 开始下注
* @return: void
* @author: 谢柯
* @time: 2022/7/25 12:10
*/
public void xiazhu(int firstPos) {
Scanner in = new Scanner(System.in);
int i1 = 0;
//逆时针下注
for (int i = firstPos; i >= 0; i--) {
if (players[i] != null) {
// 对于两种人物,有两种不同的提示状态
if (i == firstPos) {
// 第一个需要选择,下注或者不加
System.out.println(players[i] + "-开始下注\n下注-1 不加-2 放弃-3");
} else {
// 之后的选择,跟或加
System.out.println(players[i] + "-开始下注\n跟-1 加-2 放弃-3");
}
// 菜单选择
int p = in.nextInt();
switch (p) {
case 1:
// 如果是首位,输入下注筹码
if (i == firstPos) {
System.out.println("输入值:");
i1 = in.nextInt();
while (i1 > moneyHand[i] || i1 > 5 * NUM || i1<0) {
System.out.println("错误,重新输入");
i1 = in.nextInt();
}
// 如果值符合规范
// 值不小0,值小于账户余额,值小于最大值(5*入场费)
money[i] = money[i] - i1;
moneyHand[i] = moneyHand[i] + i1;
} else {
// 如果不是首位,赋上一位的值
money[i] = money[i] - i1;
moneyHand[i] = moneyHand[i] + i1;
}
break;
case 2:
if (i == firstPos) {
// 首位选择不加,给一个初始值
i1 = 200;
} else {
// 除过第一位,其余玩家加情况,
int i2 = in.nextInt();
while (i2 < i1 || i2 > moneyHand[i] || i2 > 5 * NUM) {
System.out.println("错误,重新输入");
i2 = in.nextInt();
}
// 将输入的金额同步到数组
money[i] = money[i] - i2;
moneyHand[i] = moneyHand[i] + i2;
}
break;
case 3:
// 如果玩家需要放弃,则需要将玩家列表中删除,但筹码不能删除,考虑如何处理
// 如果玩家放弃,则清除该玩家在”玩家“数组中的位置
players[i] = null;
break;
default:
System.out.println("错误");
i++;
}
}
}
//逆时针下注
for (int i = players.length - 1; i > firstPos; i--) {
if (players[i] != null) {
// 对于两种人物,有两种不同的提示状态
if (i == firstPos) {
// 第一个需要选择,下注或者不加
System.out.println(players[i] + "-开始下注\n下注-1 不加-2 放弃-3");
} else {
// 之后的选择,跟或加
System.out.println(players[i] + "-开始下注\n跟-1 加-2 放弃-3");
}
// 菜单选择
int p = in.nextInt();
switch (p) {
case 1:
// 如果是首位,输入下注筹码
if (i == firstPos) {
System.out.println("输入值:");
i1 = in.nextInt();
while (i1 > moneyHand[i] || i1 > 5 * NUM || i1<0) {
System.out.println("错误,重新输入");
i1 = in.nextInt();
}
// 如果值符合规范
// 值不小0,值小于账户余额,值小于最大值(5*入场费)
money[i] = money[i] - i1;
moneyHand[i] = moneyHand[i] + i1;
} else {
// 如果不是首位,赋上一位的值
money[i] = money[i] - i1;
moneyHand[i] = moneyHand[i] + i1;
}
break;
case 2:
if (i == firstPos) {
// 首位选择不加,给一个初始值
i1 = 200;
} else {
// 除过第一位,其余玩家加情况,
int i2 = in.nextInt();
while (i2 < i1 || i2 > moneyHand[i] || i2 > 5 * NUM) {
System.out.println("错误,重新输入");
i2 = in.nextInt();
// 将输入的金额同步到数组
money[i] = money[i] - i2;
moneyHand[i] = moneyHand[i] + i2;
}
}
break;
case 3:
// 如果玩家需要放弃,则需要将玩家列表中删除,但筹码不能删除,考虑如何处理
// 如果玩家放弃,则清除该玩家在”玩家“数组中的位置
players[i] = null;
break;
default:
System.out.println("错误,重新输入");
i++;
}
}
}
}
// 查看筹码值,无实际意义
public void fori() {
System.out.println("剩余金额:");
for (int i = 0; i < money.length; i++) {
if (money[i] != null) {
System.out.print(players[i] + ":" + money[i] + " ");
}
}
System.out.println("\n");
System.out.println("抛出金额:");
for (int i = 0; i < moneyHand.length; i++) {
if (money[i] != null) {
System.out.print(players[i] + ":" + moneyHand[i] + " ");
}
}
System.out.println("\n");
}
public static void main(String[] args) {
// 实例化本类对象
ShowHand sh = new ShowHand();
// 添加玩家
sh.initPlayer("电脑玩家", "孙悟空", "孙悟空1", "孙悟空2");
// 洗牌
sh.initCards();
// 初始化玩家手牌(为空)
sh.initPlayerCards();
//下面测试所有扑克牌,没有实际作用
sh.showAllCards();
System.out.println("---------------");
//从孙悟空开始发牌,每人一张
sh.deliverCard("孙悟空");
// 查看第一张手牌
sh.showOnewCards();
//看所有玩家手上的牌
sh.showPlayerCards();
/*
这个地方需要增加处理:
1.游戏是否只剩一个玩家?如果是,则此玩家胜利了
2.如果已经是最后一张扑克牌,则需要比较剩下玩家的牌面大小
*/
//再次从"电脑玩家"开始派牌
// 请注意,如果在上一轮该玩家放弃,则这里将抛出索引越界,可能的一种解决方案是,依次向数组后遍历,不为空开始 发牌
sh.deliverCard("电脑玩家");
// 查看玩家手牌
sh.showPlayerCards();
// 查看牌面,最大者下注
sh.bet();
// 查看筹码流动
sh.fori();
//再次从"电脑玩家"开始派牌
sh.deliverCard("电脑玩家");
// 查看玩家手牌
sh.showPlayerCards();
// 查看牌面,最大者下注
sh.bet();
// 查看筹码流动
sh.fori();
// 第三次发牌
// sh.deliverCard("电脑玩家");
//
// sh.showPlayerCards();
}
}
后言:
当我们试图想用文明道德这样的一个利刃,去指向她人的生活的时候,请不妨停下来想一想,我们在用谁的道德标准,绑架的又是谁的生活。
在复杂的现实与社会的分层之中,请更为公允的去思考道德秩序这样的问题,而在不同的生活境遇与生计可能性之下,也要正视不同人的活法,也更为善意地去对待我们与她人的关系
——黄盈盈