斗地主游戏java底层实现
gitee地址
介绍
本文介绍了斗地主小游戏玩法规则,以及详细的java代码底层实现。 采用命令行模式,可实现3人斗地主,简单模拟斗地主的全部功能,玩法规则和qq斗地主类似。由于重点不在于界面效果,所以主要习java开发的基本功,比如工厂模式创建对象,ThreadLocal应用,随机算法,轮询算法实现,java8 stream编程等。感兴趣的小伙伴可以进来瞅一瞅,欢迎大家留言指教。
软件架构
软件架构说明
使用jdk1.8以上版本,maven项目构建,项目中使用了lombok插件
主要类说明
- CardMain 程序启动类,main方法运行
- CardApp 游戏入口,启动,结束等方法
- BaseContext 全局参数容器,包含上家标识(身份,回答标识),上家打出的牌型和牌数,当前玩家标识(身份)
- PlatformManager 平台管理器 初始化纸牌,洗牌,发牌,注册容器,管理玩家等方法
- PlayerGeneral 玩家模型生成器
- PlayerModel 玩家模型 定义接受牌,重组展示手牌,出牌等方法
- RuleFactory 规则工厂 主要创建各种规则类
- CardNumRule 纸牌数量规则,平台发完牌后开始检验
- CardTypeRule 纸牌类型规则 校验出的牌是否符合设定的规则
- CardCompareRule 大小规则 比较玩家出的牌大小
使用说明
1.规则
牌数规则
1.一共54张牌
2.每人17张
3.底牌三张
单张以上的请用逗号分隔
牌型规则
1.可以出单张
2.可以出一对
3.可以出三连张
4.可以出三带一
5.可以出三带二
6.可以出顺子
6.可以出连对
7.可以出飞机
8.可以出炸弹
9.可以出王炸
2.说明
-系统首先生成一个平台管理器,然后生成三个玩家,平台管理器负责管理整副牌以及所有玩家信息,比如洗牌,发牌,获取指定玩家,获取下一个玩家等
-玩家主要负责出牌
-当某一个玩家打出最后一张牌时,游戏结束,开始结算
3.流程
游戏启动->初始化->洗牌->发牌->抢地主->轮流出牌->结算
相关代码
- CardApp 游戏入口,启动,结束等方法
/**
* @description: 游戏入口
* @author: ly
* @date: 2020/8/20 19:10
*/
public class CardApp implements App {
@Override
public void start() throws Exception {
//游戏界面打印
printAppInfo();
//游戏规则打印
printAppRule();
//平台初始化,洗牌,注册容器
PlatformManager manager = (PlatformManager) PlatformManager.build().cardInit().shuffle().register(new PlatformContext());
//开始发牌
manager.giveCards(PlayerGeneral.general("小明"), PlayerGeneral.general("小帅"), PlayerGeneral.general("小王"));
//平台发牌校验
RuleFactory.create(CardNumRule.class).checkCardNum(manager);
//开始抢地主
grabDiZhu(manager);
//轮番出牌
roundLeave(manager);
}
private void roundLeave(PlatformManager manager) {
//地主玩家开始出牌
PlayerModel player = manager.getPlayer(BaseContext.getContext().getDiZhuId());
//设置当前出牌玩家
PlayerModel currPlayer;
while (true) {
currPlayer = StringUtils.isBlank(BaseContext.getContext().getCurrId()) ? player : manager.getNextPlayer();
//打印提示信息
printNoticeInfo(manager, currPlayer);
Scanner scanner = new Scanner(System.in);
String cards = scanner.nextLine();
//出牌
if (!currPlayer.leave(cards)) {
continue;
}
//是否已经出完
if (currPlayer.getCardList().size() == 0) {
manager.settle(currPlayer);
break;
}
BaseContext.getContext().setCurrId(currPlayer.getId());
//必须放到这里,否则容易清屏多度
this.cleanConsole();
}
this.finished();
}
private void printNoticeInfo(PlatformManager manager, PlayerModel currPlayer) {
//预获取下一个玩家,重要
String currId = BaseContext.getContext().getCurrId();
BaseContext.getContext().setCurrId(currPlayer.getId());
System.out.println("轮到玩家:" + currPlayer.getId() + "[" + currPlayer.getMark() + "]" + " 出牌" + " ===>" + currPlayer.order(currPlayer.getCardList()));
System.out.println("提示:要不起请输入命令[pass],否则直接输入要打出的牌,多个请用空格分隔");
if (StringUtils.isNotBlank(BaseContext.getContext().getPreId())) {
System.out.println("提示:上一个玩家是 "+ BaseContext.getContext().getPreId() +
"["+ BaseContext.getContext().getPreMark() + "]"+ " 打出的牌["+ BaseContext.getContext().getPreGiveCards()+"]" +
" 下一轮玩家 "+manager.getNextPlayer().getId() + "["+manager.getNextPlayer().getMark()+"]");
}
//设置回去,重要
BaseContext.getContext().setCurrId(currId);
}
private void grabDiZhu(PlatformManager manager) throws Exception {
//随机获取一个玩家
PlayerModel randomPlayer = manager.getRandomPlayer();
//第一个玩家id
String firstId = randomPlayer.getId();
//打印玩家信息
randomPlayer.print();
//设置当前玩家标识
BaseContext.getContext().setCurrId(randomPlayer.getId());
//设置当前玩家
PlayerModel currPlayer = null;
while (true) {
System.out.println("***开始抢地主 命令[yes/no]***");
Scanner scanner = new Scanner(System.in);
String command = scanner.nextLine();
if (!command.equalsIgnoreCase(Command.YES) && !command.equalsIgnoreCase(Command.NO)) {
System.out.println("!!!请输入正确的命令[yes/no]!!!");
continue;
}
if (command.equalsIgnoreCase(Command.YES)) {
if (currPlayer == null) {
currPlayer = randomPlayer;
}
//设置地主标识
currPlayer.setDiZhu(true);
break;
} else {
//获取下一个玩家
currPlayer = manager.getNextPlayerRound(firstId);
if (currPlayer == null) {
break;
}
//打印玩家信息
currPlayer.print();
//设置当前玩家标识
BaseContext.getContext().setCurrId(currPlayer.getId());
}
}
//是否抢地主成功
if (currPlayer != null && currPlayer.isDiZhu()) {
grabSucc(manager, currPlayer);
} else {
grabFail();
}
}
/**
* 抢地主成功
*
* @param manager 平台管理器
* @param currPlayer 玩家
*/
private void grabSucc(PlatformManager manager, PlayerModel currPlayer) {
System.out.println("恭喜你,抢地主成功!");
System.out.println("===========================================================");
BaseContext.init();
BaseContext.getContext().setDiZhuId(currPlayer.getId());
//将底牌给当前玩家
currPlayer.getCardList().addAll(manager.getBottom());
}
/**
* 抢地主失败
*
* @throws Exception 异常
*/
private void grabFail() throws Exception {
//抢地主失败
System.out.println("!没有人抢地主,是否重启游戏?[yes/no]");
Scanner scanner = new Scanner(System.in);
String command = scanner.nextLine();
if (command.equalsIgnoreCase(Command.YES)) {
start();
} else {
finished();
}
}
private void printAppRule() throws InterruptedException {
System.out.println("=============================开始介绍游戏规则===================================");
System.out.println("本次游戏为三人局,包含抢地主,每人起手17张牌,底牌三张,地主可以拿到剩余底牌...");
Thread.sleep(1000);
System.out.println("每个玩家可以出单张,一对,三连张,三带一,三带二,顺子,连对,炸弹,王炸等牌型...");
Thread.sleep(1000);
System.out.println("每个玩家出的牌如果有多张,必须用空格分离...");
Thread.sleep(1000);
System.out.println("当上一个玩家打出牌后,下家可以根据提示选择相应命令或者打出对应牌型的牌...");
Thread.sleep(1000);
System.out.println("王炸>4张炸弹>其他...");
System.out.println("=================================================================================");
Thread.sleep(1000);
System.out.println("游戏启动中,请稍等...");
Thread.sleep(3000);
}
private void printAppInfo() throws InterruptedException {
System.out.println("***********************欢迎进入小Y斗地主,祝您游戏体验愉快**********************");
Thread.sleep(1000);
}
@Override
public void finished() {
System.out.println("******游戏结束******");
System.exit(1);
}
@Override
public void cleanConsole() {
for (int i = 0; i < 20; i++) {
System.out.println();
}
}
}
- BaseContext 全局参数容器,包含上家标识(身份,回答标识),上家打出的牌型和牌数,当前玩家标识(身份)
/**
* @description: 基础参数容器
* @author: ly
* @date: 2020/8/24 17:25
*/
public class BaseContext {
public static ThreadLocal<PlatformContext> threadLocal = new ThreadLocal<>();
public static void setContext(PlatformContext context) {
threadLocal.set(context);
}
public static PlatformContext getContext() {
return threadLocal.get();
}
/**
* 移除context
*/
public static void remove() {
threadLocal.remove();
}
/**
* 初始化context
*/
public static void init() {
threadLocal.remove();
setContext(new PlatformContext());
}
}
/**
* @description: 平台全局参数
* 全局变量参数,上家标识(身份,回答标识),上家打出的牌型和牌数,当前玩家标识(身份)
* @author: ly
* @date: 2020/8/21 10:58
*/
@Data
public class PlatformContext implements Context {
/**
* 上家标识
*/
private String preId;
/**
* 上家身份
*/
private String preMark;
/**
* 上家牌型
*/
private CardTypeEnum preType;
/**
* 上家打出的牌,空格分隔
*/
private String preGiveCards;
/**
* 当前玩家id
*/
private String currId;
/**
* 地主id
*/
private String diZhuId;
}
/**
* @description: 定义全局参数容器
* @author: ly
* @date: 2020/8/20 17:06
*/
public interface Context {
}
- PlatformManager 平台管理器 初始化纸牌,洗牌,发牌,注册容器,管理玩家等方法
/**
* @description: 纸牌管理器接口
* 定义洗牌,发牌方法,结算方法
* @author: ly
* @date: 2020/8/20 17:04
*/
public interface Manager {
/**
* 纸牌初始化
*
* @return Manager
*/
Manager cardInit();
/**
* 洗牌
*
* @return Manager
*/
Manager shuffle();
/**
* 给多个玩家一次发牌
*
* @param players 玩家数组
* @return Manager
*/
Manager giveCards(AbstractPlayer... players);
/**
* 结算
*
* @param player 当前玩家
* @return Manager
*/
Manager settle(AbstractPlayer player);
/**
* 注册全局参数
*
* @param context 全局参数
* @return Manager
*/
Manager register(PlatformContext context);
/**
* 根据id获取玩家
*
* @param id 玩家id
* @return 具体玩家
*/
PlayerModel getPlayer(String id);
/**
* 获取随机玩家
*
* @return 具体玩家
*/
PlayerModel getRandomPlayer();
/**
* 获取下一个玩家
*
* @return 具体玩家
*/
PlayerModel getNextPlayer();
/**
* 获取下一个玩家,只获取一轮
* @param firstId 从哪个玩家开始算
* @return 具体玩家
*/
PlayerModel getNextPlayerRound(String firstId);
}
/**
* @description: 平台管理器
* 管理洗牌,发牌方法,结算方法,管理所有玩家
* @author: ly
* @date: 2020/8/20 18:04
*/
@Data
public class PlatformManager implements Manager {
/**
* 纸牌容器
*/
private List<String> cardContainer = new ArrayList<>();
/**
* 底牌
*/
private List<String> bottom = new ArrayList<>();
/**
* 玩家数组
*/
private AbstractPlayer[] playerArr = new AbstractPlayer[]{};
/**
* 玩家Map key:id
*/
private Map<String, Player> playerMap = new HashMap<>();
/**
* 初始化纸牌容器
*
* @return Manager
*/
@Override
public Manager cardInit() {
//初始化54张牌
cardContainer.addAll(CardConst.CARD_POINTS);
cardContainer.addAll(CardConst.CARD_POINTS);
cardContainer.addAll(CardConst.CARD_POINTS);
cardContainer.addAll(CardConst.CARD_POINTS);
cardContainer.addAll(CardConst.CARD_SPE);
System.out.println("纸牌初始化成功!");
return this;
}
/**
* 洗牌
*
* @return Manager
*/
@Override
public Manager shuffle() {
Collections.shuffle(cardContainer);
System.out.println("洗牌完成!");
return this;
}
/**
* 发牌,给多个玩家发牌,轮询发
*
* @param players 玩家数组
* @return Manager
*/
@Override
public Manager giveCards(AbstractPlayer... players) {
System.out.println("发牌中......");
if (cardContainer.size() > 0 && players.length > 0) {
//定义轮询初始化值
int lx = players.length;
for (int i = 0; i < cardContainer.size() - CardConst.BOTTOM_NUM; i++) {
AbstractPlayer player = players[lx++ % players.length];
player.accept(cardContainer.get(i));
}
//将剩余的牌放入底牌
bottom.addAll(cardContainer.subList(cardContainer.size() - CardConst.BOTTOM_NUM, cardContainer.size()));
//存放玩家
playerArr = players;
playerMap.putAll(Arrays.stream(players).collect(Collectors.toMap(AbstractPlayer::getId, a -> a)));
System.out.println("发牌完毕!");
} else {
System.out.println("!!!纸牌或玩家创建失败!!!");
}
return this;
}
/**
* 结算
*
* @param player 当前玩家
* @return Manager
*/
@Override
public Manager settle(AbstractPlayer player) {
if (player.getCardNum() == 0) {
System.out.println("玩家:" + player.getId() + " 获胜。");
}
return this;
}
/**
* 注册全局容器
*
* @param context 全局参数
* @return Manager
*/
@Override
public Manager register(PlatformContext context) {
BaseContext.setContext(context);
return this;
}
/**
* 构建实体
*
* @return PlatformManager
*/
public static PlatformManager build() {
return new PlatformManager();
}
/**
* 根据id获取玩家
*
* @param id 玩家id
* @return 具体玩家
*/
@Override
public PlayerModel getPlayer(String id) {
return (PlayerModel) this.playerMap.get(id);
}
/**
* 获取随机玩家
*
* @return 具体玩家
*/
@Override
public PlayerModel getRandomPlayer() {
return (PlayerModel) this.playerArr[(new Random().nextInt(playerArr.length))];
}
/**
* 获取下一个玩家
*
* @return 下一个具体玩家
*/
@Override
public PlayerModel getNextPlayer() {
//当前玩家id
String currId = BaseContext.getContext().getCurrId();
if (StringUtils.isBlank(currId)) {
System.out.println("!!没有设置当前玩家id,随机获取一个!!");
return getRandomPlayer();
}
//下一个玩家id,默认第一个
String next = playerArr[0].getId();
for (int i = 0; i < this.playerArr.length - 1; i++) {
if (this.playerArr[i].getId().equalsIgnoreCase(currId)) {
if (i == this.playerArr.length - 1) {
next = playerArr[0].getId();
} else {
next = playerArr[i + 1].getId();
}
break;
}
}
return getPlayer(next);
}
/**
* 获取下一个玩家,只获取一轮
*
* @param firstId 从哪个玩家开始算
* @return 下一个具体玩家
*/
@Override
public PlayerModel getNextPlayerRound(String firstId) {
for (; ; ) {
PlayerModel nextPlayer = getNextPlayer();
if (nextPlayer.getId().equals(firstId)) {
return null;
}
return nextPlayer;
}
}
}
- PlayerGeneral 玩家模型生成器
/**
* @description: 玩家生成器
* @author: ly
* @date: 2020/8/20 18:05
*/
public class PlayerGeneral {
public static AbstractPlayer general(String id) {
return new PlayerModel(id);
}
}
- PlayerModel 玩家模型 定义接受牌,重组展示手牌,出牌等方法
/**
* @description: 玩家接口
* 定义接收牌、清牌、出牌方法
* @author: ly
* @date: 2020/8/20 17:52
*/
public interface Player {
/**
* 接收一张牌
*
* @param card 牌
*/
void accept(String card);
/**
* 接收集合
*
* @param cardList 牌集合
*/
void accept(List<String> cardList);
/**
* 清理牌
*
* @param cardList 牌集合
*/
List<String> order(List<String> cardList);
/**
* 打印自身信息
*/
void print();
/**
* 出牌
* @param cards 打出的牌
* @return true:出牌成功
*/
boolean leave(String cards);
}
/**
* @description: 玩家抽象类
* @author: ly
* @date: 2020/8/20 17:02
*/
public abstract class AbstractPlayer implements Player{
/**
* 玩家标识
*/
protected String id;
/**
* 玩家持有的牌
*/
protected List<String> cardList;
/**
* 当前牌的数量
*/
protected int cardNum;
public String getId() {
return id;
}
protected AbstractPlayer(String id) {
this.id = id;
}
public List<String> getCardList() {
return cardList;
}
public int getCardNum() {
return cardList.size();
}
}
/**
* @description: 玩家
* @author: ly
* @date: 2020/8/20 17:39
*/
@Setter
@Getter
public class PlayerModel extends AbstractPlayer {
/**
* 是否地主
*/
private boolean diZhu;
public PlayerModel(String id) {
super(id);
cardList = new ArrayList<>();
}
@Override
public void accept(String card) {
cardList.add(card);
}
@Override
public void accept(List<String> cardList) {
cardList.addAll(cardList);
}
@Override
public List<String> order(List<String> cardList) {
return CardUtil.cartRecomShow(cardList);
}
@Override
public void print() {
System.out.println("玩家:" + getId() + "-" + getMark());
System.out.println("纸牌:" + order(cardList) + ",牌数:" + getCardNum());
}
@Override
public boolean leave(String cards) {
try {
//是否pass
if (StringUtils.isNotBlank(cards) && cards.equalsIgnoreCase(Command.PASS)) {
return true;
}
//出牌规则校验
CardTypeEnum cardTypeEnum = RuleFactory.create(CardTypeRule.class).checkCardType(cards, this);
if (cardTypeEnum == null) {
System.out.println("错误:!出牌有误,请检查打出的牌!");
return false;
}
PlatformContext context = BaseContext.getContext();
//是否第一个出牌
if (StringUtils.isBlank(context.getPreId()) || this.getId().equalsIgnoreCase(context.getPreId())) {
//出牌成功,设置context
updateContext(cards, cardTypeEnum, context);
return true;
}
//比较出牌大小
if (!RuleFactory.create(CardCompareRule.class).compare(context, cardTypeEnum, cards)) {
System.out.println("错误:!出牌有误,请检查打出的牌!");
return false;
}
//出牌成功,设置context
updateContext(cards, cardTypeEnum, context);
return true;
} catch (Exception e) {
System.out.println("未识别命令,请重新输入");
return false;
}
}
/**
* 更新容器
*
* @param cards 打出的牌
* @param cardTypeEnum 牌型
* @param context 容器
*/
private void updateContext(String cards, CardTypeEnum cardTypeEnum, PlatformContext context) {
context.setPreId(this.getId());
context.setPreMark(this.getMark());
context.setPreType(cardTypeEnum);
context.setPreGiveCards(cards);
//除去自己手中已经打出的牌,可能将手中已有相同的牌全部清除了,比如打出3 手上有两个3,只需要去掉一张3即可
List<String> leaveList = CardUtil.leaveConvert(cards);
List<String> cardList = this.getCardList();
boolean remove = false;
for (int i = cardList.size()-1; i>=0; i--) {
for (int j = leaveList.size()-1; j >=0; j--) {
if (leaveList.get(j).equalsIgnoreCase(cardList.get(i))) {
leaveList.remove(j);
remove = true;
break;
}
}
if (remove) {
cardList.remove(i);
remove = false;
}
}
}
@Override
public String toString() {
return "玩家:" + getId() + ",纸牌:" + cardList + ",牌数:" + cardNum + ",是否地主:" + diZhu;
}
/**
* 获取标识
*
* @return 地主或农民
*/
public String getMark() {
return isDiZhu() ? "地主" : "农民";
}
}
- RuleFactory 规则工厂 主要创建各种规则类
/**
* @description: 规则工厂
* @author: ly
* @date: 2020/8/21 18:25
*/
public class RuleFactory {
public static <T> T create(Class<T> t) {
try {
return t.newInstance();
} catch (Exception e) {
System.out.println("!!!出错了!!!");
System.exit(1);
return null;
}
}
}
- CardNumRule 纸牌数量规则,平台发完牌后开始检验
/**
* @description: 牌数规则
* 牌数规则 发牌完成校验,平台管理
* 1.一共54张牌
* 2.每人17张
* 3.底牌三张
* @author: ly
* @date: 2020/8/21 16:36
*/
public class CardNumRule implements Rule {
/**
* 检查牌数
*
* @param manager 管理平台
*/
public void checkCardNum(PlatformManager manager) {
List<String> cardContainer = manager.getCardContainer();
if (cardContainer == null || cardContainer.size() != CardConst.TOTAL_NUM) {
System.out.println("!!!出错了,牌数不为54张!!!");
System.exit(1);
}
AbstractPlayer[] playerArr = manager.getPlayerArr();
boolean everyNum = Arrays.stream(playerArr).anyMatch(player -> player.getCardNum() != CardConst.EVERY_NUM);
if (everyNum) {
System.out.println("!!!出错了,每人初始牌数应该17张!!!");
System.exit(1);
}
List<String> bottom = manager.getBottom();
if (bottom == null || bottom.size() != CardConst.BOTTOM_NUM) {
System.out.println("!!!出错了,底牌不为3张!!!");
System.exit(1);
}
}
}
- CardTypeRule 纸牌类型规则 校验出的牌是否符合设定的规则
/**
* @description: 牌型规则
* 牌型规则 出牌校验
* 1.可以出单张 1张
* 2.可以出一对 2张
* 3.可以出三连张 3张
* 4.可以出三带一 4张
* 5.可以出三带二 5张
* 6.可以出顺子 5-12张
* 7.可以出连对 6-8-10-12-14-16-18
* 8.可以出飞机
* 9.可以出炸弹 4张
* 10.可以出王炸 2张
* 牌型相同 单张,一对,三连张,三带一,三带二,顺子,飞机,炸弹,王炸
* 牌型不同,必须是炸弹
* @author: ly
* @date: 2020/8/21 16:50
*/
public class CardTypeRule implements Rule {
/**
* 检查牌的类型
*
* @param cards 打出的牌
* @return 检查牌的类型
*/
public CardTypeEnum checkCardType(String cards, PlayerModel playerModel) {
if (StringUtils.isBlank(cards)) {
return null;
}
//转成可比较点数
List<String> cardList = CardUtil.leaveConvert(cards);
//最多不能超过自己的牌数
if (cardList.size() > playerModel.getCardList().size()) {
return null;
}
//出的牌是否合规
if (!cardList.stream().allMatch(this::cardOfPool)) {
return null;
}
//是否是自己的牌
if (!playerModel.getCardList().containsAll(cardList)) {
return null;
}
//单张
if (cardList.size() == 1) {
return CardTypeEnum.SINGLE;
}
//两张
if (cardList.size() == 2) {
//一对
if (this.sameCardNum(cardList, 2)) {
return CardTypeEnum.PAIR;
}
//王炸
if (this.kingBomb(cardList)) {
return CardTypeEnum.KING_BOMB;
}
return null;
}
//三张
if (cardList.size() == 3) {
if (this.sameCardNum(cardList, 3)) {
return CardTypeEnum.THREE_PAIR;
}
return null;
}
//四张
if (cardList.size() == 4) {
//三带一
if (this.sameCardNum(cardList, 3)) {
return CardTypeEnum.THREE_BELT_ONE;
}
//炸弹
if (this.sameCardNum(cardList, 4)) {
return CardTypeEnum.BOMB;
}
return null;
}
//5张
if (cardList.size() == 5) {
//三带二
if (this.sameCardNum(cardList, 3) && this.sameCardNum(cardList, 2)) {
return CardTypeEnum.THREE_BELT_TWO;
}
//顺子
if (this.shunZi(cardList)) {
return CardTypeEnum.SHUN_ZI;
}
return null;
}
// 大于5张
if (cardList.size() > 5) {
//是否顺子
if (this.shunZi(cardList)) {
return CardTypeEnum.SHUN_ZI;
}
//是否连对
if (this.evenPair(cardList)) {
return CardTypeEnum.EVEN_PAIR;
}
//是否飞机
if (this.plane(cardList)) {
return CardTypeEnum.PLANE;
}
}
return null;
}
/**
* 校验出的牌是否在点数池中
*
* @param card 单张牌
* @return true:满足
*/
private boolean cardOfPool(String card) {
if (StringUtils.isNotBlank(card)) {
return CardConst.POINTS_POOL.contains(card);
}
return false;
}
/**
* 是否有n张相同的牌
*
* @param cardList 打出的牌集合
* @param num 相同牌数
* @return true:是
*/
private boolean sameCardNum(List<String> cardList, int num) {
Map<String, List<String>> cardMap = cardList.stream().collect(Collectors.groupingBy(String::toString));
return cardMap.values().stream().anyMatch(list -> list.size() == num);
}
/**
* 是否王炸
*
* @param cardList 打出的牌
* @return true:王炸
*/
private boolean kingBomb(List<String> cardList) {
if (cardList.size() == 2) {
return 16 + 17 == Integer.parseInt(cardList.get(0)) + Integer.parseInt(cardList.get(1));
}
return false;
}
/**
* 是否顺子
* 差=1,最大点数必须小于15
*
* @param cardList 转换后的牌
* @return true:顺子
*/
private boolean shunZi(List<String> cardList) {
List<String> sortCard = CardUtil.sort(cardList);
//最后一张牌必须小于15
if (Integer.parseInt(sortCard.get(sortCard.size() - 1)) >= 15) {
return false;
}
boolean shunZi = true;
for (int i = 0; i < sortCard.size(); i++) {
if (i < sortCard.size() - 1) {
if ((Integer.parseInt(sortCard.get(i + 1)) - Integer.parseInt(sortCard.get(i))) != 1) {
shunZi = false;
break;
}
}
}
return shunZi;
}
/**
* 连对
* 1.最少6张
* 2.最大点数必须小于15
* 3.去重后是顺子
* 4.去重后的每张牌必须在原牌中存在两张
*
* @param cardList 转换后的牌
* @return true:连对
*/
private boolean evenPair(List<String> cardList) {
if (cardList.size() < 6 || Integer.parseInt(cardList.get(cardList.size() - 1)) >= 15) {
return false;
}
//去重后的牌
Set<String> cardSet = new HashSet<>(cardList);
if (!shunZi(new ArrayList<>(cardSet))) {
return false;
}
Map<String, List<String>> cardMap = cardList.stream().collect(Collectors.groupingBy(String::toString));
for (String next : cardSet) {
if (!cardMap.containsKey(next)) {
return false;
}
if (cardMap.get(next).size() != 2) {
return false;
}
}
return true;
}
/**
* 是否飞机
* 1.三连去重必须是顺子
* 2. 去掉三连后必须n个单张,或者n个一对
*
* @param cardList 转换后的牌
* @return true:飞机
*/
private boolean plane(List<String> cardList) {
Map<String, List<String>> cardMap = cardList.stream().collect(Collectors.groupingBy(String::toString));
//三连张容器
List<String> threeList = new ArrayList<>();
List<String> allThreeList = new ArrayList<>();
cardMap.forEach((card, list) -> {
if (list.size() == 3) {
threeList.add(card);
allThreeList.addAll(list);
}
});
if (threeList.size() <2) {
return false;
}
//三连是否是顺子
if (!this.shunZi(threeList)) {
return false;
}
//副本
List<String> backList = new ArrayList<>(cardList);
backList.removeAll(allThreeList);
//剩余是否单张
if (backList.size() == threeList.size()) {
return true;
}
//剩余是否对子
if (backList.size() == 2 * threeList.size()) {
Map<String, List<String>> backMap = backList.stream().collect(Collectors.groupingBy(String::toString));
return backMap.values().stream().allMatch(back -> back.size() == 2);
}
return false;
}
}
- CardCompareRule 大小规则 比较玩家出的牌大小
/**
* @description: 大小规则 出票校验,玩家模型
* * 单张:比大小
* * 一对:比单张大小
* * 三带一:比三连的单张大小
* * 三带二:比三连的单张大小
* * 顺子:牌数相等,并且比较起始点大小
* * 飞机:牌数相等,比较飞机起始点
* * 炸弹:比单张大小
* * 王炸>4张炸弹>其他
* @author: ly
* @date: 2020/8/21 16:51
*/
public class CardCompareRule implements Rule {
/**
* 出票大小比较
*
* @param context 容器
* @param typeEnum 打出的牌型
* @param cards 打出的牌
* @return true:当前牌大
*/
public boolean compare(PlatformContext context, CardTypeEnum typeEnum, String cards) {
String preGiveCards = context.getPreGiveCards();
//转成可比较点数
List<String> cardList = CardUtil.leaveConvert(cards);
List<String> preList = CardUtil.leaveConvert(preGiveCards);
if (typeEnum == KING_BOMB) {
return true;
}
if (context.getPreType() == SINGLE
|| context.getPreType() == PAIR
|| context.getPreType() == THREE_PAIR
|| context.getPreType() == THREE_BELT_ONE
|| context.getPreType() == THREE_BELT_TWO
|| context.getPreType() == PLANE) {
if (typeEnum == CardTypeEnum.BOMB) {
return true;
}
return compareOne(context.getPreType(), typeEnum, getSameCard(preList), getSameCard(cardList)) && preList.size() == cardList.size();
}
if (context.getPreType() == SHUN_ZI || context.getPreType() == EVEN_PAIR) {
if (typeEnum == CardTypeEnum.BOMB) {
return true;
}
return compareOne(context.getPreType(), typeEnum, preList.get(0), cardList.get(0)) && preList.size() == cardList.size();
}
if (context.getPreType() == BOMB) {
return compareOne(context.getPreType(), typeEnum, preList.get(0), cardList.get(0));
}
return false;
}
/**
* 获取相同牌数多的单张牌,比如一对获取单张,三带一获取三个中的单张,三带二获取三个中的单张
*
* @param cardList 牌集合
* @return 相同牌数多的单张牌
*/
private String getSameCard(List<String> cardList) {
Map<String, List<String>> cardMap = cardList.stream().collect(Collectors.groupingBy(String::toString));
int size = 1;
String currCard = null;
for (Map.Entry<String, List<String>> entry : cardMap.entrySet()) {
String card = entry.getKey();
List<String> list = entry.getValue();
if (list.size() >= size) {
size = list.size();
currCard = card;
}
}
return cardMap.get(currCard).get(0);
}
/**
* 比较单张
*
* @param preType 前玩家牌型
* @param currType 当前玩家牌型
* @param preCard 前玩家出的牌
* @param currCard 当前玩家出的牌
* @return true: 当前玩家大
*/
private boolean compareOne(CardTypeEnum preType, CardTypeEnum currType, String preCard, String currCard) {
return preType == currType && Integer.parseInt(currCard) > Integer.parseInt(preCard);
}
}
全部代码请前往gitee地址下载