单机斗地主之完整功能初版

一、斗地主规则介绍

1.1 基本规则

参加人数:3人

总牌数:54张,从2到A,四种花色("梅花", "红桃", "黑桃", "方形"),共52张,加黑白色的小王,彩色的大王。

分牌数:每人先分17张,最后确定地主拿3张

胜利规则:三人中有一人出完牌

1.2 出牌规则

单张:任意一张牌

对子:两张一样的牌

顺子:大于四张,连续的牌

三带一:三张一样的牌带任意一张

炸弹:四张一样,或双王

1.3 大小规则

单张:从3到10,J,Q,K,A,2,小王,大王依次增大

对子:通单张规则

三带一:三张牌满足单张规则

顺子:最小牌满足单张规则

炸弹:具有通吃属性,王炸无敌,四炸满足单张规则

二、代码实现规则

2.1 定义扑克牌

每张扑克牌应该具有自己的花色与值

 /**
     * 定义扑克牌
     */
    class Card implements Comparable<Card> {
        /**
         * 花色(黑桃、红桃、梅花、方块)
         */
        private String color;
        /**
         * 大小(2-10,J,Q,K,A,大王,小王)
         */
        private String value;

        public Card() {
        }

        public Card(String color, String value) {
            this.color = color;
            this.value = value;
        }

        public String getColor() {
            return color;
        }

        public void setColor(String color) {
            this.color = color;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Card) {
                Card card = (Card) obj;
                if (card.value.equalsIgnoreCase(this.value) && card.color.equalsIgnoreCase(this.color)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public int hashCode() {
            return color.hashCode() + value.hashCode();
        }

        //定义大小规则
        @Override
        public int compareTo(Card card) {
            return findIndex(this) - findIndex(card);
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Card{");
            sb.append("color='").append(color).append('\'');
            sb.append(", value='").append(value).append('\'');
            sb.append('}');
            return sb.toString();
        }

    }

2.2 定义玩家

每个玩家具有自己的游戏名,在游戏中具有自己的上家与下家,持有牌与预出牌,并记录当前是否具有出牌资格

   /**
     * 定义玩家
     */
    static class Player {

        /**
         * 用户名
         */
        private String username;

        /**
         * 是否为第一个出牌
         */
        private boolean first;

        /**
         * 持有牌
         */
        LinkedList<Card> cards = new LinkedList<>();

        /**
         * 当前选择出牌
         */
        LinkedList<Card> putCards = new LinkedList<>();

        /**
         * 上一位出牌人
         */
        private Player before;
        /**
         * 下一位出牌人
         */
        private Player next;

        public Player() {
        }

        public Player(String username) {
            this.username = username;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }


        public void setCards(LinkedList<Card> cards) {
            this.cards = cards;
        }

        public boolean isFirst() {
            return first;
        }

        public void setFirst(boolean first) {
            this.first = first;
        }

        public LinkedList<Card> getCards() {
            return cards;
        }

        public LinkedList<Card> getPutCards() {
            return putCards;
        }

        public void setPutCards(LinkedList<Card> putCards) {
            this.putCards = putCards;
        }

        public Player getBefore() {
            return before;
        }

        public void setBefore(Player before) {
            this.before = before;
        }

        public Player getNext() {
            return next;
        }

        public void setNext(Player next) {
            this.next = next;
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Player{");
            sb.append("username='").append(username).append('\'');
            sb.append(", cards=").append(cards);
            sb.append(", putCards=").append(putCards);
            sb.append('}');
            return sb.toString();
        }
    }

2.3 定义游戏规则

一场完整的斗地主,需要洗牌,分牌,确定地主,判断出牌是否大于上两家

/**
 * 玩扑克牌
 *
 * @author sunyiran
 * @date 2018-10-19
 */
public class PlayCards {

    /**
     * 扑克牌的值
     */
    private static final String[] cardVlues = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2", "小王",
            "大王"};

    /**
     * 扑克牌的花色
     */
    private static final String[] cardColors = {"梅花", "红桃", "黑桃", "方形"};

    /**
     * 一副扑克牌
     */
    private ArrayList<Card> cardList = new ArrayList<>(54);

    /**
     * 参加玩家
     */
    private Player[] players;

    public PlayCards() {
    }

    public PlayCards(Player[] players) {
        getCardArr();
        this.players = players;
    }

    /**
     * 获取一副扑克牌的算法
     */
    private void getCardArr() {
        //前52张牌具有花色
        for (int i = 0; i < 52; i++) {
            Card card = new Card();
            card.setColor(cardColors[i % 4]);
            card.setValue(cardVlues[i % 13]);
            cardList.add(card);
        }

        cardList.add(new Card("黑白", "小王"));
        cardList.add(new Card("彩色", "大王"));
    }
 

    /**
     * 获取扑克牌大小位置
     *
     * @param card
     * @return
     */
    private int findIndex(Card card) {
        for (int i = 0; i < cardVlues.length; i++) {
            if (card.value.equalsIgnoreCase(cardVlues[i])) {
                return i;
            }
        }
        return -1;
    }


    /**
     * 洗牌
     *
     * @param player 上轮赢家
     */
    private List<Card> shuffle(Player player) {
        //清除玩家手里的牌
        Arrays.stream(players).forEach(x -> {
            LinkedList<Card> cards = x.cards;
            cards.clear();
        });
        //赢家取消优先级
        if (player != null)
            player.setFirst(false);
        //采用随机策略洗牌
        Random random = new Random();
        for (int i = 0; i < cardList.size(); i++) {
            Card temp = cardList.get(i);
            //要交换的索引
            int changeIndex = random.nextInt(54);
            cardList.set(i, cardList.get(changeIndex));
            cardList.set(changeIndex, temp);

        }
        return cardList;
    }

    /**
     * 分牌
     */
    private void distributional() {
        //留下三张给地主
        for (int i = 0; i < 51; i++) {
            if (i < 17) {
                players[0].getCards().add(cardList.get(i));

            }
            if (i >= 17 && i < 34) {
                players[1].getCards().add(cardList.get(i));
            }
            if (i >= 34) {
                players[2].getCards().add(cardList.get(i));
            }
        }
        //随机确定地主
        players[new Random().nextInt(3)].setFirst(true);
        //地主拿三张
        Arrays.stream(players).filter(Player::isFirst).forEach(x -> {
            LinkedList<Card> cards = x.getCards();
            cards.add(cardList.get(51));
            cards.add(cardList.get(52));
            cards.add(cardList.get(53));

            System.out.println("当前地主: " + x.getUsername());
        });
        //按顺序排序
        players[0].getCards().sort(Card::compareTo);
        players[1].getCards().sort(Card::compareTo);
        players[2].getCards().sort(Card::compareTo);

    }

    /**
     * 判断出牌是否合法(根据出牌规则)
     *
     * @param player
     * @return 个位代表出牌个数,个位相同时,值高低也代表大小级别(可以用Map优化)
     */
    private int compliance(Player player) {

        LinkedList<Card> cards = player.getPutCards();
        LinkedList<Card> hasCards = player.getCards();
        //可以不出牌
        if (cards.size() == 0) {
            return 0;
        }

        //每张牌必须是自己手里的牌
        long count = cards.stream().filter(x -> !hasCards.contains(x)).count();
        if (count > 0) {
            return -1;
        }

        //如果是单张牌,只要在牌中即可
        if (cards.size() == 1) {
            return 1 + (findIndex(cards.get(0)) + 1) * 10;
        }

        //如果是两张,则为对子或王炸
        if (cards.size() == 2 && cards.contains(new Card("彩色", "大王")) && cards.contains(new Card("黑白", "小王"))) {
            return 20000002;
        }

        if (cards.size() == 2 && cards.get(0).value.equals(cards.get(1).value)) {
            return 2 + (findIndex(cards.get(0)) + 1) * 100;
        }

        //如果是四张,则为三带一,或者炸弹
        //判断是否为炸弹
        boolean bomb = cards.get(0).value.equals(cards.get(1).value) && cards.get(1).value.equals(cards.get(2)
                .value) && cards.get(2).value.equals(cards.get(3).value);
        if (cards.size() == 4) {
            if (bomb) {
                return 4 + (findIndex(cards.get(0)) + 1) * 100000;
            } else {
                //偷懒,第一张牌不允许为单张
                return 4 + (findIndex(cards.get(0)) + 1) * 1000;
            }
        }

        //大于四张,则为连子
        if (cards.size() > 4) {
            for (int i = 1; i < cards.size(); i++) {
                if (findIndex(cards.get(i)) != findIndex(cards.get(i - 1)) + 1) {
                    return -1;
                }
            }
            return cards.size() + (findIndex(cards.get(0)) + 1) * 1000;
        }

        return -1;

    }

    /**
     * 判断出牌是否合法(根据大小规则)
     *
     * @param player
     * @return
     */
    private boolean compareSize(Player player) {

        //出牌一定比上两家大
        //自己
        int self = compliance(player);
        String v1 = String.valueOf(self);
        //上方第一家
        int before1 = compliance(player.before);

        String v2 = String.valueOf(self);
        //上方第二家
        int before2 = compliance(player.before.before);
        String v3 = String.valueOf(self);

        if (before1 == 0 && before2 == 0 && self > 0) {
            return true;
        }
        if (self == 0) {
            return true;
        }

        int amout1 = Integer.parseInt(v1.substring(v1.length() - 2));
        int amout2 = Integer.parseInt(v2.substring(v2.length() - 2));
        int amout3 = Integer.parseInt(v3.substring(v3.length() - 2));

        if (self == 20000002) {
            return true;
        }

        if (before1 > 0 && (amout1 == amout2 || self > 100003) && self > before1) {
            return true;
        }
        if (before1 == 0 && before2 > 0 && (amout1 == amout3 || self > 100003) && self > before2) {
            return true;
        }

        System.out.println("您的出牌没能大于上家");
        return false;
    }
}

2.4 定义游戏流程

玩家在控制台输入想要打出的牌,一行一张牌,输入end结束。每次会显示上两家出牌记录,已经自身当前持有牌组

    /**
     * 比赛流程
     * @param players 参加的玩家
     */
    public static void start(Player[] players) {
//        //定义玩家
//        Player p1 = new Player("一号");
//        Player p2 = new Player("二号");
//        Player p3 = new Player("三号");
//
//        p1.setNext(p2);
//        p1.setBefore(p3);
//        p2.setNext(p3);
//        p2.setBefore(p1);
//        p3.setNext(p1);
//        p3.setBefore(p2);

        //开局
        PlayCards play = new PlayCards(players);
        //洗牌
        play.shuffle(null);
        //分牌
        play.distributional();

        Scanner sc = new Scanner(System.in);
        boolean exit = play.players[0].getCards().size() > 0 && play.players[1].getCards().size() > 0 && play
                .players[2].getCards().size() > 0;
        while (exit) {
            Player player = Arrays.stream(play.players).filter(Player::isFirst).findFirst().get();
            //显示上两家出牌
            System.out.println("上两家出牌为");
            System.out.println(player.before.getPutCards());
            System.out.println(player.before.before.getPutCards());
            //当前玩家输出的一组牌
            LinkedList<Card> putCards = player.getPutCards();
            //从手里清除打出的牌
            player.getCards().removeAll(putCards);
            //清除上次出牌记录
            putCards.clear();
            System.out.println(player.getUsername() + "玩家请出牌");
            System.out.println("您当前持有的卡牌:" + player.getCards());
            //控制台输入输出的每张牌
            String line;
            while (!(line = sc.nextLine()).equalsIgnoreCase("end")) {

                String[] split = line.split("[:|:]");
                if (split.length != 2) {
                    System.out.println("请按规则输入");
                    continue;
                }
                Card card = play.new Card(split[0], split[1]);
                putCards.add(card);
            }

            //如果当前玩家出牌符合规定,则下一位玩家出牌,否则重新出牌
            if (play.compliance(player) > -1 && play.compareSize(player)) {
                player.setFirst(false);
                player.getNext().setFirst(true);
            } else {
                putCards.clear();
                System.out.println("请选择正确的牌组重新出牌");
            }
        }

    }

三、体验斗地主

更多规则测试,请下载源码,在本地运行

四、扩展方向

相对于完整的网络斗地主:

1、图形界面:可以采用Swing做简单开发,也可以基于安卓或IOS做App开发,当然Web界面也是不错的

2、网络图形:可以用socket通信实现多人在线游戏

猜你喜欢

转载自blog.csdn.net/qq_35813653/article/details/83240576