源码如下:
package selfpractice.day4;
import java.util.*;
//多线程模拟抢地,重点代码位于loot()方法内
public class Practice_Poker {
public static void main(String[] args) {//入口
//获得一幅扑克牌存放于HashMap中
HashMap<Integer, String> myPoker = newPoker();
//洗牌后将扑克牌键值存放于链表中
LinkedList<Integer> myPokerPoint = shuffle();
//存放玩家扑克牌键值的链表,每个链表代表一个玩家
LinkedList<Integer> player1 = new LinkedList<>();
LinkedList<Integer> player2 = new LinkedList<>();
LinkedList<Integer> player3 = new LinkedList<>();
//起牌按现实世界获得17张扑克牌,剩余3张即底牌
getPoker(myPokerPoint,player1,player2,player3);
LinkedList<Integer> diPai = new LinkedList<>(myPokerPoint);//lambda不让传入可变参数myPoker 展示底牌放到方法执行后
loot(myPokerPoint,player1,player2,player3);//抢地主
//展示地底牌
for (Integer integer : diPai) {
System.out.print(myPoker.get(integer)+" ");
}
System.out.println();//换个行 对齐
try {//等待控制台输出完毕前面的内容,否则结果可能格式混乱
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//三位玩家理牌
System.out.println("三位玩家理牌...");
sort(player1);
sort(player2);
sort(player3);
//展示牌面,最后揭晓底牌
System.out.print("玩家一:");
showPoker(player1,myPoker);
System.out.print("玩家二:");
showPoker(player2,myPoker);
System.out.print("玩家三:");
showPoker(player3,myPoker);
}
//获得扑克牌方法:来一幅扑克牌,返回一个包含54张牌的HashMap数组,键值升序0~53,对应斗地主规则牌面值大小
private static HashMap<Integer,String> newPoker(){
HashMap<Integer,String> hashMap=new HashMap<>();//存放扑克牌的HashMap
String[] colors={"♥","♠","♦","♣"};//花色数组
String[] nums={"3","4","5","6","7","8","9","10","J","Q","K","A","2"};//牌面数组
int term=0;//键值
//遍历加入前52张牌
for (String num:nums) {
for (String color:colors) {
hashMap.put(term++,color+num);
}
}
//添加大小王
hashMap.put(term++,"小王");
hashMap.put(term,"大王");
System.out.println("得到一副牌");
return hashMap;
}
//洗牌方法,实际洗的是LinkedList数组
private static LinkedList<Integer> shuffle(){
//获得所有牌的键值存入链表
LinkedList<Integer> linkedList=new LinkedList<>();
for (int i = 0; i < 54; i++) {
linkedList.add(i);
}
Collections.shuffle(linkedList);//洗牌
System.out.println("洗牌...");
return linkedList;//返回洗牌结果
}
//发牌方法:每起牌一次,扑克牌少一张,真实模拟,剩余三张为底牌(参数一次为洗好的底牌和三个链表玩家)
private static void getPoker( LinkedList<Integer> myPokerPoint,LinkedList<Integer> player1, LinkedList<Integer> player2,LinkedList<Integer> player3){
LinkedList<LinkedList<Integer>> playerList=new LinkedList<>();
//玩家放入循环起牌队列
playerList.add(player1);
playerList.add(player2);
playerList.add(player3);
//循环依次起牌,各17张
for (int i = 0; i < 51; i++) {
playerList.getFirst().add(myPokerPoint.removeFirst());
playerList.addLast(playerList.removeFirst());//循环起牌队列滚动一次,即第一个元素放到最后
}
System.out.println("发牌...");
}
//理牌方法:用Collections工具的快速排序sort方法
private static void sort(LinkedList<Integer> linkedList){
Collections.sort(linkedList);
}
//抢地主:利用多线程模拟抢地主,传入
private static void loot(LinkedList<Integer> myPokerPoint,LinkedList<Integer> player1, LinkedList<Integer> player2,LinkedList<Integer> player3) {
Object lock1=new Object();//对象锁1
//先吹口哨,等待0.5秒,三位玩家线程依次就绪
synchronized (lock1){System.out.println("多线程模拟抢地主...\n裁判吹口哨:");
//等待0.5秒
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("口哨声...");
lock1.notifyAll();//释放对象锁1开始抢地主
}
new Thread(()->{//加载线程后等待对象锁1倍裁判程序释放,抢到对象锁开始执行同步代码
synchronized (lock1){
//若底牌数大于0则拿过来加入自己的牌,并表示抢到地主并展示一下
//若没有底牌就算了
if(myPokerPoint.size()>0) {
player1.addAll(myPokerPoint);
System.out.println("玩家一抢到地主");
System.out.print("展示底牌:");
myPokerPoint.clear();
}
}
},"玩家一线程").start();
new Thread(()->{//加载线程后等待对象锁1倍裁判程序释放
synchronized (lock1){
//若底牌数大于0则拿过来加入自己的牌,并表示抢到地主并展示一下
//若没有底牌就算了
if(myPokerPoint.size()>0) {
player2.addAll(myPokerPoint);
System.out.println("玩家二抢到地主");
System.out.print("展示底牌:");
myPokerPoint.clear();
}
}
},"玩家二线程").start();
new Thread(()->{//加载线程后等待对象锁1倍裁判程序释放
synchronized (lock1){
//若底牌数大于0则拿过来加入自己的牌,并表示抢到地主并展示一下
//若没有底牌就算了
if(myPokerPoint.size()>0) {
player3.addAll(myPokerPoint);
System.out.println("玩家三抢到地主");
System.out.print("展示底牌:");
myPokerPoint.clear();
}
}
},"玩家三线程").start(
);
}
//亮牌方法:自动判断传入的是玩家还是底牌
private static void showPoker(LinkedList<Integer> linkedList, HashMap<Integer, String> hashMap){
if(linkedList.size()==20) System.out.print("(地主)");
for (int i = 0; i <20; i++) {
if(linkedList.size()==20){//若是地主
String str=hashMap.get(linkedList.get(i));
System.out.print(str+" ");
}else{//否则是农民
String str=hashMap.get(linkedList.get(i));
System.out.print(str+" ");
if(i==16) break;
}
}
System.out.println();//换行
}
}
连续运行三次结果如下:
第一次:
得到一副牌
洗牌...
发牌...
多线程模拟抢地主...
裁判吹口哨:
口哨声...
玩家三抢到地主
展示底牌:♦10 ♠9 ♠6
三位玩家理牌...
玩家一:♥5 ♠5 ♣5 ♦6 ♠7 ♦7 ♠8 ♥9 ♦9 ♣9 ♥10 ♠10 ♦J ♣Q ♣K ♥2 小王
玩家二:♣3 ♥4 ♣4 ♦5 ♥6 ♣6 ♥7 ♣7 ♣8 ♣10 ♥J ♣J ♠K ♦K ♣A ♦2 大王
玩家三:(地主)♥3 ♠3 ♦3 ♠4 ♦4 ♠6 ♥8 ♦8 ♠9 ♦10 ♠J ♥Q ♠Q ♦Q ♥K ♥A ♠A ♦A ♠2 ♣2
第二次:
得到一副牌
洗牌...
发牌...
多线程模拟抢地主...
裁判吹口哨:
口哨声...
玩家一抢到地主
展示底牌:大王 ♦4 ♦6
三位玩家理牌...
玩家一:(地主)♥3 ♦4 ♣4 ♠6 ♦6 ♠7 ♦7 ♦8 ♣8 ♥9 ♦9 ♣10 ♦J ♣J ♠A ♣A ♥2 ♠2 小王 大王
玩家二:♣3 ♥5 ♠5 ♦5 ♣5 ♥7 ♣7 ♥8 ♣9 ♦10 ♥Q ♦Q ♠K ♦K ♣K ♥A ♣2
玩家三:♠3 ♦3 ♥4 ♠4 ♥6 ♣6 ♠8 ♠9 ♥10 ♠10 ♥J ♠J ♠Q ♣Q ♥K ♦A ♦2
第三次:
得到一副牌
洗牌...
发牌...
多线程模拟抢地主...
裁判吹口哨:
口哨声...
玩家二抢到地主
展示底牌:♥9 ♠8 ♦4
三位玩家理牌...
玩家一:♥3 ♣4 ♥5 ♠5 ♥6 ♣6 ♦8 ♣8 ♣9 ♥10 ♦10 ♣10 ♦J ♣Q ♥K ♥A ♠A
玩家二:(地主)♠3 ♥4 ♦4 ♦5 ♣5 ♦6 ♥7 ♠7 ♠8 ♥9 ♠9 ♥J ♠J ♣J ♠Q ♦Q ♠2 ♦2 ♣2 大王
玩家三:♦3 ♣3 ♠4 ♠6 ♦7 ♣7 ♥8 ♦9 ♠10 ♥Q ♠K ♦K ♣K ♦A ♣A ♥2 小王
机缘巧合,各抢到一次地主!!!!大家可以自己复制代码运行.
思维过程已经用在代码中注释出,可以复制代码后利用调试模式跟随我的思路一步步执行代码!
编程思想:
1/ 利用多线程模拟抢地主,加入"裁判程序",率先获得同步对象锁. 以保障三个玩家线程全部创建完毕后,再同时争夺对象锁,执行锁内代码,可以保证竞争绝对公平;
2/因为java控制台字符输出有延迟,故中间抢完地主后置了一段等待后台输出的时间;
3/根据本代码思维,利用HashMap的双列集合特点,可以完成任何像扑克牌这种无法用自然排序集合的自定义排序;