一、本期教学目标
- 掌握map集合常用方法。
- 掌握map集合使用场景。
- 通过map集合实现斗地主洗牌发牌动作。
二、代码实现
1️⃣创建数字与扑克字符的映射
组装一副由54张扑克组成的扑克牌,由编号按斗地主牌面值顺序进行排序,说明一下,这样有序排列,目的是最后好按斗地主牌排序输出,如果乱序,怎么知道斗地主的牌面排序是如何呢?完全没有规则,所以从这里就得确定排序规则。
接下来就是对该小点实现步骤分析:
- 创建一个map字典,目的是用于存放编码与扑克牌面值。
- 分别创建扑克中的 花色集合 与 数字集合,目的是为了排列组合所有的扑克牌值。
- 列举扑克中所包含的所有花色与数字。
- 大小王先写入map字典中,编号从0开始。
- 接着再分别将所有的花色与数字组合按集合顺序有序写进。
具体该创建map字典完整代码实现如下:
/**
* 组装54张扑克牌(扑克字典)
* 用编号按牌的顺序组装好。
*/
private Map<Integer, String> buildPorkMap() {
//1创建map字典
Map<Integer, String> pokerMap = new HashMap<>();
//2创建扑克中的 花色集合 与 数字集合
List<String> colors = new ArrayList<>();
List<String> numbers = new ArrayList<>();
//3列举扑克中的花色与数字
Collections.addAll(colors, "♦", "♣", "♥", "♠");
Collections.addAll(numbers, "2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
//4大小王特例先存进map字段中
int count = 0;
pokerMap.put(count++, "大王");
pokerMap.put(count++, "小王");
//5组合花色与数字
for (String number : numbers) {
for (String color : colors) {
//花色+数字
String card = color + number;
pokerMap.put(count++, card);
}
}
return pokerMap;
}
由于扑克牌中大小王是最大,且不跟花色数字一样,所以就单独进行map写入,大王为0,小王为1,其次就是根据扑克数字集合进行双遍历排列组合,编号就每次加1,这样就保证了同牌面值越大编号则越小,牌面值越小的值越大。
扑克字典顺序(pokerMap)展示如下:
具体完整pokerMap打印如下:
pokerMap:{0=大王, 1=小王, 2=♦2, 3=♣2, 4=♥2, 5=♠2, 6=♦A, 7=♣A, 8=♥A, 9=♠A, 10=♦K, 11=♣K, 12=♥K, 13=♠K, 14=♦Q, 15=♣Q, 16=♥Q, 17=♠Q, 18=♦J, 19=♣J, 20=♥J, 21=♠J, 22=♦10, 23=♣10, 24=♥10, 25=♠10, 26=♦9, 27=♣9, 28=♥9, 29=♠9, 30=♦8, 31=♣8, 32=♥8, 33=♠8, 34=♦7, 35=♣7, 36=♥7, 37=♠7, 38=♦6, 39=♣6, 40=♥6, 41=♠6, 42=♦5, 43=♣5, 44=♥5, 45=♠5, 46=♦4, 47=♣4, 48=♥4, 49=♠4, 50=♦3, 51=♣3, 52=♥3, 53=♠3}
这样你们直观的看, 是不是就一目了然,花色因为不需要考虑顺序,所以对于花色集合防御内循环,外循环有牌面值来固定,对于每一个牌面值它都有4种花色,这是扑克的特点,这大家别忽略掉了。
2️⃣洗牌
接下来就是洗牌动作了,所谓的洗牌就是把一副牌打乱,那我们按什么来固定一副牌呢,很简单,你只需要确定一个list集合,而其中的元素就是一副牌中的所有编号,然后将装有编号的list进行元素位置打乱调整即可。
接下来就是对该小点实现步骤分析:大家请看
- 先从map字典中取出所有的扑克编号porkSet。
- 定义一个存放扑克编号的list集合。
- 将porkSet全部添加到list集合中。
- 对list集合进行打乱操作。
具体实现洗牌动作完整代码实现如下:
/**
* 洗牌
*/
private List<Integer> flushPork() {
//先取出所有的扑克编号
Set<Integer> porkNumMap = this.buildPorkMap().keySet();
//存放打乱后的扑克编号
List<Integer> porkList = new ArrayList<>();
//存放到porkList中
porkList.addAll(porkNumMap);
//进行打乱
Collections.shuffle(porkList);
return porkList;
}
顺序打乱前我们可以明显看到获取到的正是map字典的所有字典编号。
而我们执行Collections.shuffle()方法,目的就是将上述所获取到的map字典编号进行顺序打乱,我们可以从下方截图得知:是否被全部打乱?具体请看如下截图,咱们一见分晓。
很明显,将所获取到的map字典所有的编号已打乱,目的不就是洗牌么。
打印洗牌后的打印结果:很明显是无序的把。
porkList:[2, 11, 20, 51, 24, 5, 33, 27, 21, 45, 22, 17, 50, 44, 15, 10, 1, 19, 6, 39, 49, 12, 37, 35, 23, 7, 47, 41, 25, 38, 31, 46, 8, 32, 28, 9, 48, 34, 16, 52, 43, 18, 4, 30, 0, 53, 14, 36, 29, 26, 40, 3, 13, 42]
3️⃣发牌
洗完牌,接着就是给三名玩家进行发牌了。发牌规则就是每名玩家轮流摸牌,每名玩家各17张牌,且摸到第17轮时停手,剩余3张作为底牌。
至于如何代码实现发牌动作,接下来就是对该小点实现步骤分析:大家请看
- 定义4个list集合,分别存放玩家123及底牌。
- 对上述进行洗牌后的porkList进行遍历。完成三个玩家交替摸牌。
- 遍历porkList,遍历步长为1。
- 如何控制3人轮流摸牌?这好办呀,这我们可以这么做,取遍历下标index,对3进行取模依次判断模值是否为0、1、2,不就实现了一人一模。
- 注意,如何确定三张底牌?最简单的做法就是判断当index >=51时,则表示循环已经是第18轮循环,对最后获取到的三张牌写入底牌list集合中即可。
具体实现发牌动作完整代码实现如下:
/**
* 发牌
*
* @param porkList 随机牌
* @param playerNo1 玩家1牌
* @param playerNo2 玩家2牌
* @param playerNo3 玩家3牌
* @param dipaiNo 底牌
*/
private void sendPork(List<Integer> porkList, List<Integer> playerNo1, List<Integer> playerNo2, List<Integer> playerNo3, List<Integer> dipaiNo) {
for (int i = 0; i < porkList.size(); i++) {
Integer pno = porkList.get(i);
//留三张底牌
if (i >= 51) {
dipaiNo.add(pno);
continue;
}
//给三名玩家发牌
if (i % 3 == 0) {
playerNo1.add(pno);
continue;
} else if (i % 3 == 1) {
playerNo2.add(pno);
continue;
} else {
playerNo3.add(pno);
continue;
}
}
}
我们可以来看下,我们三名玩家且底牌的获取是否满足一副完整牌。
数量总数相加是54,这是毫无疑问的。至于里头的编号是否有重复的,我们可以来检验一下,哪怕是通过肉眼也行啊。
大家请看下边的排序展示,这对于肉眼排查也是最直接的啊。
4️⃣玩家各手牌牌按编号排序
由于map字典是按斗地主大小排序的,所以对于这几名玩家及底牌,我们可以直接用Collections集合工具类提供的sort()方法一键排序。就非常的方便快捷。
//4.将三玩家的手牌按编号排序(排完序自然是斗地主排列顺序)
Collections.sort(playerNo1);
Collections.sort(playerNo2);
Collections.sort(playerNo3);
Collections.sort(dipaiNo);
System.out.println("playerNo1:" + playerNo1);
System.out.println("playerNo2:" + playerNo2);
System.out.println("playerNo3:" + playerNo3);
System.out.println("dipaiNo:" + dipaiNo);
发牌后手动按斗地主牌规则排完序的打印结果:
playerNo1:[2, 4, 6, 10, 12, 23, 29, 31, 32, 33, 41, 45, 48, 50, 51, 52, 53]
playerNo2:[1, 7, 11, 14, 22, 24, 25, 26, 27, 28, 30, 34, 37, 39, 43, 44, 46]
playerNo3:[0, 5, 8, 9, 15, 16, 17, 18, 19, 20, 21, 35, 36, 38, 40, 47, 49]dipaiNo:[3, 13, 42]
5️⃣牌由编号转牌面值
由于我们这里对玩家的手牌只是存储的map字典表中的编号,如果需要真实展示,那肯定是需要对编号进行转扑克牌面值吧,这对于大家来说,这一步非常的简单,具体如何做呢?
- 定义一个方法,入参为list手牌。
- 遍历list手牌,根据遍历所取到的编号从map字典中获取porkMap所对应的牌面值。
具体代码实现如下:
/**
* 牌转扑克字母
*
* @param pokerMap 扑克字典
* @param porkNo 玩家手牌
*/
private List<String> transformPork(Map<Integer, String> pokerMap, List<Integer> porkNo) {
List<String> porkStr = new ArrayList<>();
for (Integer porkno : porkNo) {
porkStr.add(pokerMap.get(porkno));
}
return porkStr;
}
而对于该方法,我们就要手动调用4次,依次传入玩家123及底牌集合。
然后打印结果如下:
玩家1手牌:[♦2, ♥2, ♦A, ♦K, ♥K, ♣10, ♠9, ♣8, ♥8, ♠8, ♠6, ♠5, ♥4, ♦3, ♣3, ♥3, ♠3]
玩家2手牌:[小王, ♣A, ♣K, ♦Q, ♦10, ♥10, ♠10, ♦9, ♣9, ♥9, ♦8, ♦7, ♠7, ♣6, ♣5, ♥5, ♦4]
玩家3手牌:[大王, ♠2, ♥A, ♠A, ♣Q, ♥Q, ♠Q, ♦J, ♣J, ♥J, ♠J, ♣7, ♥7, ♦6, ♥6, ♣4, ♠4]
底牌:[♣2, ♠K, ♦5]
6️⃣展示所有玩家及底牌
由于步骤5已经对底牌进行了转换,我们便可以直接对该转后的集合分别进行打印输出:
具体代码实现如下:
List<String> player1 = this.transformPork(porkMap, playerNo1);
List<String> player2 = this.transformPork(porkMap, playerNo2);
List<String> player3 = this.transformPork(porkMap, playerNo3);
List<String> dipai = this.transformPork(porkMap, dipaiNo);
//6.查看。
System.out.println("玩家1手牌:" + player1);
System.out.println("玩家2手牌:" + player2);
System.out.println("玩家3手牌:" + player3);
System.out.println("底牌:" + dipai);
转成扑克牌字符结果如下:
玩家1手牌:[♦2, ♥2, ♦A, ♦K, ♥K, ♣10, ♠9, ♣8, ♥8, ♠8, ♠6, ♠5, ♥4, ♦3, ♣3, ♥3, ♠3]
玩家2手牌:[小王, ♣A, ♣K, ♦Q, ♦10, ♥10, ♠10, ♦9, ♣9, ♥9, ♦8, ♦7, ♠7, ♣6, ♣5, ♥5, ♦4]
玩家3手牌:[大王, ♠2, ♥A, ♠A, ♣Q, ♥Q, ♠Q, ♦J, ♣J, ♥J, ♠J, ♣7, ♥7, ♦6, ♥6, ♣4, ♠4]
底牌:[♣2, ♠K, ♦5]
如下是完整控制台打印内容:
7️⃣总结
如上就是完整的通过代码模拟斗地主的洗牌发牌到展示牌的这一系列动作,不知道你对于该模拟场景有何思考?对于里头的那一阶段遇到了问题或者有难度的,都可以评论区进行交流呀,不懂咱就问。
要我来说就是要分析理清楚每一个环节分别做什么事,比如从刚开始的map字典,这一步,如果你能想到,并且还通过编号来确定斗地主牌面值排序,就已经完成了百分之五十,其次就是能运用到Collections所提供的shuffle()、sort().其实也不枉费我这段期间给大家的免费教学map集合相关知识点啦,其实这一期也是对map双列集合的一个实地联系加具体业务联合使用,对于你在日后的业务开发中,也能有一定的业务锻炼及方法选型起到一定的启蒙锻炼。
不要说写的这些练习,压根实际业务开发中就遇不到,这并不是说业务写到的东西就才是用用的,大错特错,千万别这样想啊,兄弟们,现在教学的都是身为一名java开发必须要掌握的,这并不是跟大家开玩笑,毕竟我就是这么过来的。