• 不到一个月独自一人开发斗地主游戏(h5 + 安卓 + 苹果)


    开篇感言

    不枉我深入学习基础知识,算法与数据结构,编译原理,java并发编程,这些东西都有用得到的时候。

    再极端的时间内学习了cocos creator引擎,并学以致用,有极大的增加了自己的自信心。

    在公司总是拧螺丝,一个人开发一个完成的作品,我又觉得自己彷佛回到了色彩斑斓的世界中。

    前端代码地址:https://github.com/neroHua/neroGameClient
    后端代码地址:https://github.com/neroHua/neroGameServer

    背景

    本人使用java工作已经7年之多了,js和ts基本懂一些。
    打算开发一款逮狗腿子的棋牌游戏(合同学们玩),先仿制一款斗地主游戏,试一下。

    涉及到的知识点技能

    编码功底:

    1. 算法和数据结构
    2. 设计模式(策略,责任链)
    3. 敏捷开发。

    游戏引擎:
    1.Cocos creator 2.0.4

    网络通信:
    1. http
    2. websocket

    编程语言:
    1.java (后端)
    2.typeScript(前端)

    后端:
    1.spring,springMVC,spring boot, mybatis
    2.flyway,jwt,h2,junit

    前端:
    h5

    架构设计及其思路

    1. 服务器整体为单机结构(以后有需要,再扩容好了),提供 h5,安卓,苹果 三种客户端接入服务器,且前端由Cocos 引擎处理,直接打包成三个端 。
    2. 客户端和服务器端的通信协议选择为http,和 websocket (三端都支持的通信方式)。用户点击等等事件由客户端发送http请求。通过websocket,服务器向客户端推送消息。服务器端不接收用户通过websocket推送到服务器的消息。只用用户进入房间以后,才会开启websocket通信。
    3. 游戏的主要模型/功能设计。用户,房间,游戏,游戏控制器,游戏回合。房间是核心: 用户再房间里面玩游戏。每一个房间都有一个游戏控制器用以控制游戏的进程。用户有手牌。游戏控制器,通过游戏回合控制每回合的进行。

    详细设计

    1 单张卡牌设计

    为了便于,排序,比较,等操作,设计如下:

    这里的code呢,等于卡牌前端的图片名称(不过我这里前端使用的图集)。
    这里的value呢,代表了单张卡牌的权值大小,可用于比较牌面的大小。
    这里的message呢,代表了卡牌的中文名称。
    这里不需要花色,所以没有设计花色字段

    package com.nero.hua.enumeration;
    
    import lombok.Getter;
    
    @Getter
    public enum CardEnumeration {
    
        CARD_103("card_103", 3, "方块3"),
        CARD_104("card_104", 4, "方块4"),
        CARD_105("card_105", 5, "方块5"),
        CARD_106("card_106", 6, "方块6"),
        CARD_107("card_107", 7, "方块7"),
        CARD_108("card_108", 8, "方块8"),
        CARD_109("card_109", 9, "方块9"),
        CARD_110("card_110", 10, "方块10"),
        CARD_111("card_111", 11, "方块J"),
        CARD_112("card_112", 12, "方块Q"),
        CARD_113("card_113", 13, "方块K"),
        CARD_114("card_114", 14, "方块1"),
        CARD_115("card_115", 15, "方块2"),
        CARD_203("card_203", 3, "梅花3"),
        CARD_204("card_204", 4, "梅花4"),
        CARD_205("card_205", 5, "梅花5"),
        CARD_206("card_206", 6, "梅花6"),
        CARD_207("card_207", 7, "梅花7"),
        CARD_208("card_208", 8, "梅花8"),
        CARD_209("card_209", 9, "梅花9"),
        CARD_210("card_210", 10, "梅花10"),
        CARD_211("card_211", 11, "梅花J"),
        CARD_212("card_212", 12, "梅花Q"),
        CARD_213("card_213", 13, "梅花K"),
        CARD_214("card_214", 14, "梅花1"),
        CARD_215("card_215", 15, "梅花2"),
        CARD_303("card_303", 3, "红桃3"),
        CARD_304("card_304", 4, "红桃4"),
        CARD_305("card_305", 5, "红桃5"),
        CARD_306("card_306", 6, "红桃6"),
        CARD_307("card_307", 7, "红桃7"),
        CARD_308("card_308", 8, "红桃8"),
        CARD_309("card_309", 9, "红桃9"),
        CARD_310("card_310", 10, "红桃10"),
        CARD_311("card_311", 11, "红桃J"),
        CARD_312("card_312", 12, "红桃Q"),
        CARD_313("card_313", 13, "红桃K"),
        CARD_314("card_314", 14, "红桃1"),
        CARD_315("card_315", 15, "红桃2"),
        CARD_403("card_403", 3, "黑桃3"),
        CARD_404("card_404", 4, "黑桃4"),
        CARD_405("card_405", 5, "黑桃5"),
        CARD_406("card_406", 6, "黑桃6"),
        CARD_407("card_407", 7, "黑桃7"),
        CARD_408("card_408", 8, "黑桃8"),
        CARD_409("card_409", 9, "黑桃9"),
        CARD_410("card_410", 10, "黑桃10"),
        CARD_411("card_411", 11, "黑桃J"),
        CARD_412("card_412", 12, "黑桃Q"),
        CARD_413("card_413", 13, "黑桃K"),
        CARD_414("card_414", 14, "黑桃1"),
        CARD_415("card_415", 15, "黑桃2"),
        CARD_500("card_500", 0, "背面"),
        CARD_508("card_508", 8, "狗子"),
        CARD_516("card_516", 16, "小王"),
        CARD_517("card_517", 17, "大王");
    
        private String code;
        private int value;
        private String message;
    
        CardEnumeration(String code, int value, String message) {
            this.code = code;
            this.value = value;
            this.message = message;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    2 打牌时,卡牌组合的牌型设计

    为了便于,排序,比较,等操作,设计如下:
    权值大的牌型,比权值小的牌型大。
    权值相同的牌型,需要牌型一样(牌型为同一个枚举)方可比较

    这里的code呢,一种编码。
    这里的value呢,代表的组合的权值。
    这里的message呢,代表了卡牌的中文名称。
    逮狗腿子,有些牌型不支持,还需要更多的牌型,后面再弄了,不过思路是已经想好了。

    package com.nero.hua.enumeration;
    
    import lombok.Getter;
    
    @Getter
    public enum PlayCardTypeEnumeration {
    
        SINGLE("single", 0, "单牌"),
        STRAIGHT("straight", 0, "顺子"),
    
        PAIR("pair", 0, "对子"),
        PAIR_STRAIGHT("pairStraight", 0, "连对"),
    
        TRIPLE("triple", 0, "三不带"),
        TRIPLE_SINGLE("tripleSingle", 0, "三带一"),
        TRIPLE_PAIR("triplePAIR", 0, "三带一对"),
    
        AIRPLANE("airplane", 0, "飞机不带"),
        AIRPLANE_SINGLE("airplaneSingle", 0, "飞机带单"),
        AIRPLANE_PAIR("airplanePair", 0, "飞机带对"),
    
        FOUR_SINGLE("fourSingle", 0, "4带2"),
        FOUR_PAIR("fourPair", 0, "4带2对"),
    
        BOMB("bomb", 1, "炸弹"),
        BOMB_KING("bombKing", 2, "王炸");
    
        private String code;
        private int value;
        private String message;
    
        PlayCardTypeEnumeration(String code, int value, String message) {
            this.code = code;
            this.value = value;
            this.message = message;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    3 一种通用的牌型及其比较算法

    1.为了便于比较牌型,先把牌按照一定格式格式化, 比如下面的。(对于有变种的牌型可以让客户端选择)

        /**
         * 客户端需要根据牌型把本字段格式化并排序降低服务器计算压力
         *
         * 比如
         *  顺子:7, 6, 5, 4, 3
         *  对子:5,5, 4, 4, 3, 3
         *  三带一: 3, 3, 3, 2
         *  三带对: 3, 3, 3, 2, 2
         *  飞机带单: 4, 4, 4, 3, 3, 3, 2, 1
         *  飞机带对: 4, 4, 4, 3, 3, 3, 2, 2, 1, 1
         *  王炸:大王, 小王
         *
         */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.对于格式化后的牌型,比较的办法如下:

        public static boolean currentPlayCardListBetterThanLastPlayCardList(UserPlayCardTurnMO lastUserPlayCardTurnMO, List<CardEnumeration> playCardList, PlayCardTypeEnumeration playCardTypeEnumeration) {
            if (null == lastUserPlayCardTurnMO) {
                return Boolean.TRUE;
            }
    
            PlayCardTypeEnumeration lastPlayCardTypeEnumeration = lastUserPlayCardTurnMO.getPlayCardTypeEnumeration();
            if (playCardTypeEnumeration.getValue() < lastPlayCardTypeEnumeration.getValue()) {
                return Boolean.FALSE;
            }
            else if (playCardTypeEnumeration.getValue() > lastPlayCardTypeEnumeration.getValue()) {
                return Boolean.TRUE;
            }
    
            if (lastPlayCardTypeEnumeration != playCardTypeEnumeration) {
                return Boolean.FALSE;
            }
    
            List<CardEnumeration> lastPlayCardList = lastUserPlayCardTurnMO.getCardList();
            return lastPlayCardList.size() == playCardList.size()
                    && lastPlayCardList.get(0).getValue() < playCardList.get(0).getValue();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. 牌型的格式化算法
      整体式为: 把牌按照相同单牌权值的数量的大小进行分割成小块,再根据数量的大小把小块合并。把原来的大块按照单牌权值的大小进行排序,可以方便上诉操作
        public static int formatCardList(List<CardEnumeration> playCardList) {
            Map<Integer, Integer> playCardValueCountMap = getPlayCardValueCountMap(playCardList);
    
            quickSortOneCardList(0, playCardList.size() - 1, playCardList);
    
            Map<Integer, List<CardEnumeration>> playCardCountListMap = getPlayCardCountListMap(playCardList, playCardValueCountMap);
    
            List<Integer> countList = getSortedPlayCardCountList(playCardCountListMap);
    
            for (int i = 0, k = 0; i < countList.size(); i++) {
                List<CardEnumeration> cardEnumerationList = playCardCountListMap.get(countList.get(i));
                for (int j = 0; j < cardEnumerationList.size(); j++) {
                    playCardList.set(k, cardEnumerationList.get(j));
                    k++;
                }
            }
    
            return countList.get(0);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    计算单牌权值及其数量Map

        private static Map<Integer, Integer> getPlayCardValueCountMap(List<CardEnumeration> playCardList) {
            Map<Integer, Integer> playCardValueCountMap = new HashMap<>();
    
            for (int i = 0; i < playCardList.size(); i++) {
                Integer count = playCardValueCountMap.get(playCardList.get(i).getValue());
                if (null == count) {
                    playCardValueCountMap.put(playCardList.get(i).getValue(), 1);
                }
                else {
                    playCardValueCountMap.put(playCardList.get(i).getValue(), count + 1);
                }
            }
    
            return playCardValueCountMap;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    对牌按照单牌权值递减的顺序进行排序(这里使用快排)

        public static void quickSortOneCardList(int start, int end, List<CardEnumeration> cardList) {
            if (start >= end) {
                return;
            }
    
            CardEnumeration keyCard = cardList.get(start);
            int i = start;
            int j = end;
            while (i != j) {
                if (cardList.get(i).getValue() < keyCard.getValue()) {
                    CardEnumeration temp = cardList.get(j);
                    cardList.set(j, cardList.get(i));
                    cardList.set(i, temp);
                    j--;
                }
                else {
                    i++;
                }
            }
    
            int middle = cardList.get(i).getValue() >= keyCard.getValue() ? i : i - 1;
            if (middle >= start && middle <= end) {
                CardEnumeration temp = cardList.get(middle);
                cardList.set(middle, cardList.get(start));
                cardList.set(start, temp);
            }
    
            quickSortOneCardList(start, middle - 1, cardList);
            quickSortOneCardList(middle + 1, end, cardList);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    把已经排序好的牌,按照相同单牌权值的数量,平滑分割成多个小数组

        private static Map<Integer, List<CardEnumeration>> getPlayCardCountListMap(List<CardEnumeration> sortedPlayCardList, Map<Integer, Integer> playCardValueCountMap) {
            Map<Integer, List<CardEnumeration>> playCardCountListMap = new HashMap<>();
            for (Integer i : playCardValueCountMap.keySet()) {
                playCardCountListMap.put(playCardValueCountMap.get(i), new LinkedList<>());
            }
    
            for (int i = 0; i < sortedPlayCardList.size(); i++) {
                CardEnumeration cardEnumeration = sortedPlayCardList.get(i);
                Integer count = playCardValueCountMap.get(cardEnumeration.getValue());
                List<CardEnumeration> cardEnumerationList = playCardCountListMap.get(count);
                cardEnumerationList.add(cardEnumeration);
            }
            return playCardCountListMap;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    把相同单牌权值的数量进行排序(由大到小)

        private static List<Integer> getSortedPlayCardCountList(Map<Integer, List<CardEnumeration>> playCardCountListMap) {
            List<Integer> countList = new ArrayList<>();
            for (Integer i : playCardCountListMap.keySet()) {
                countList.add(i);
            }
    
            selectionSort(countList);
            return countList;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
        public static void selectionSort(List<Integer> countList) {
            if (1 == countList.size()) {
                return;
            }
    
            for (int i = 0; i < countList.size() - 1; i++) {
                Integer current = countList.get(i);
                for (int j = i + 1; j < countList.size(); j++) {
                    Integer tobeCompared = countList.get(j);
                    if (tobeCompared > current) {
                        countList.set(i, tobeCompared);
                        countList.set(j, current);
                        current = tobeCompared;
                    }
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    把按照相同单牌权值数量分割的碎片,根据数量的大小拼接起来

            for (int i = 0, k = 0; i < countList.size(); i++) {
                List<CardEnumeration> cardEnumerationList = playCardCountListMap.get(countList.get(i));
                for (int j = 0; j < cardEnumerationList.size(); j++) {
                    playCardList.set(k, cardEnumerationList.get(j));
                    k++;
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 对格式化的牌进行牌型的识别
      可以使用责任链模式,对于变种的可以可以处理。(playCardTypeValidateList 可以通过spring或者静态代码初始化进去),这种方式比较面向对象,看着更加优雅。不过考虑牌型的概率分布我使用了另外一种方式。
            for (PlayCardTypeValidate playCardTypeValidate : playCardTypeValidateList) {
                if (playCardTypeValidate.match(formattedPlayCardList)) {
                    playCardTypeMap.put(playCardTypeValidate.getPlayCardTypeEnumeration(), formattedPlayCardList);
                    break;
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    对于格式化好的三带二可以使用下面的代码识别。

    package com.nero.hua.validate.impl;
    
    import com.nero.hua.enumeration.CardEnumeration;
    import com.nero.hua.enumeration.PlayCardTypeEnumeration;
    import com.nero.hua.validate.PlayCardTypeValidate;
    
    import java.util.List;
    
    public class AirplanePairValidate implements PlayCardTypeValidate {
    
        private static final int MIN_COUNT = 10;
        private static final int GROUP_COUNT = 5;
        private static final int TRIPLE_COUNT = 3;
    
        @Override
        public PlayCardTypeEnumeration getPlayCardTypeEnumeration() {
            return PlayCardTypeEnumeration.AIRPLANE_PAIR;
        }
    
        @Override
        public boolean match(List<CardEnumeration> cardEnumerationList) {
            if (cardEnumerationList.size() < MIN_COUNT) {
                return Boolean.FALSE;
            }
    
            if (0 != cardEnumerationList.size() % GROUP_COUNT) {
                return Boolean.FALSE;
            }
    
            for (int i = 0; i < cardEnumerationList.size() / GROUP_COUNT; i += 3) {
                if (cardEnumerationList.get(i).getValue() != cardEnumerationList.get(i + 1).getValue()
                    || cardEnumerationList.get(i + 1).getValue() != cardEnumerationList.get(i + 2).getValue()) {
                    return Boolean.FALSE;
                }
            }
    
            for (int i = 0; i < cardEnumerationList.size() / GROUP_COUNT; i += 3) {
                if (cardEnumerationList.get(i).getValue() - 1 != cardEnumerationList.get(i + 3).getValue()) {
                    return Boolean.FALSE;
                }
            }
    
            if (cardEnumerationList.get(0).getValue() >= CardEnumeration.CARD_415.getValue()) {
                return Boolean.FALSE;
            }
    
            int lastTripleStartIndex = this.calculateLastTripleStartIndex(cardEnumerationList.size());
            for (int i = lastTripleStartIndex + 3; i < cardEnumerationList.size(); i += 2) {
                if (cardEnumerationList.get(i).getValue() != cardEnumerationList.get(i + 1).getValue()) {
                    return Boolean.FALSE;
                }
            }
    
            return Boolean.TRUE;
        }
    
        private int calculateLastTripleStartIndex(int size) {
            return (size / GROUP_COUNT - 1) * TRIPLE_COUNT;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    房间的设计

    房间中由用户,游戏控制器。

    package com.nero.hua.model.room;
    
    import com.nero.hua.enumeration.CardEnumeration;
    import com.nero.hua.enumeration.PlayCardTypeEnumeration;
    import com.nero.hua.enumeration.RoomEnumeration;
    import com.nero.hua.exception.RoomException;
    import com.nero.hua.game.manager.GameManager;
    import com.nero.hua.model.user.GameUserMO;
    import lombok.Getter;
    import lombok.Setter;
    import org.springframework.util.CollectionUtils;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.List;
    
    @Setter
    @Getter
    public class RoomMO {
    
        private Long roomId;
    
        private GameManager gameManager;
    
        private List<GameUserMO> gameUserMOList = new ArrayList<>();
    
        public void joinUser(String userId) {
            if (gameUserMOList.size() > this.gameManager.getMaxUserCount()) {
                throw new RoomException(RoomEnumeration.ROOM_NOT_FOUND);
            }
    
            GameUserMO gameUserMO = new GameUserMO();
            gameUserMO.setUserId(userId);
            gameUserMO.setPrepared(Boolean.TRUE);
            gameUserMOList.add(gameUserMO);
        }
    
        public void leaveUser(String userId) {
            Iterator<GameUserMO> iterator = gameUserMOList.iterator();
            while (iterator.hasNext()) {
                GameUserMO next = iterator.next();
                if (userId.equals(next.getUserId())) {
                    iterator.remove();
                    break;
                }
            }
        }
    
        public List<String> getAllUserList() {
            List<String> userIdList = new LinkedList<>();
            for (GameUserMO gameUserMO : this.getGameUserMOList()) {
                userIdList.add(gameUserMO.getUserId());
            }
            return userIdList;
        }
    
        public List<String> getAllOtherUserList(String userId) {
            List<String> userIdList = new LinkedList<>();
            for (GameUserMO gameUserMO : this.getGameUserMOList()) {
                if (!userId.equals(gameUserMO.getUserId())) {
                    userIdList.add(gameUserMO.getUserId());
                }
            }
            return userIdList;
        }
    
        public void changeUserPrepareStatus(String userId, boolean prepared) {
            Iterator<GameUserMO> iterator = gameUserMOList.iterator();
            while (iterator.hasNext()) {
                GameUserMO next = iterator.next();
                if (userId.equals(next.getUserId())) {
                    next.setPrepared(prepared);
                    break;
                }
            }
        }
    
        public boolean empty() {
            return null == gameUserMOList ? Boolean.TRUE : CollectionUtils.isEmpty(gameUserMOList);
        }
    
        public boolean shouldNotStartGame() {
            return gameManager.shouldNotStartGame(this.gameUserMOList);
        }
    
        public void startGame() {
            this.gameManager.startGame(this.gameUserMOList);
        }
    
        public String chooseOneUserToRobLandlord() {
            return this.gameManager.chooseOneUserToRobLandlord(this.gameUserMOList);
        }
    
        public void doRob(String userId) {
            this.gameManager.doRob(userId);
        }
    
        public void doNotRob(String userId) {
            this.gameManager.doNotRob(userId);
        }
    
        public List<CardEnumeration> getLandlordCardList() {
            return this.gameManager.getLandlordCardList();
        }
    
        public void giveLandlordCardToThisGuy(String userId) {
            this.gameManager.giveLandlordCardToThisGuy(userId, this.gameUserMOList);
        }
    
        public boolean hasNextOneToStartRob() {
            return this.gameManager.hasNextOneToStartRob();
        }
    
        public String makeNextUserToStartRob() {
            return this.gameManager.makeNextUserToStartRob(this.gameUserMOList);
        }
    
        public String makeLastUserRobLandlordCard() {
            return this.gameManager.makeLastUserRobLandlordCard(this.gameUserMOList);
        }
    
        public void doPlayCard(String userId, List<CardEnumeration> cardEnumerationList, PlayCardTypeEnumeration playCardTypeEnumeration) {
            this.gameManager.doPlayCard(userId, cardEnumerationList, playCardTypeEnumeration, this.gameUserMOList);
        }
    
        public boolean thisGuyWin(String userId) {
            return this.gameManager.thisGuyWin(userId, this.gameUserMOList);
        }
    
        public String makeNextUserToStartPlayCard() {
            return this.gameManager.makeNextUserToStartPlayCard(this.gameUserMOList);
        }
    
        public void doNotPlayCard(String userId) {
            this.gameManager.doNotPlayCard(userId);
        }
    
        public boolean hasNextOneToStartPlayCard() {
            return this.gameManager.hasNextOneToStartPlayCard();
        }
    
        public String makeLastPlayCardUserToStartPlayCard() {
            return this.gameManager.makeLastPlayCardUserToStartPlayCard();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147

    游戏控制器与回合的设计

    回合实体类和gameManager配合完成,回合开始,回合结束,下一个该谁打牌

    package com.nero.hua.game.manager;
    
    import com.nero.hua.enumeration.CardEnumeration;
    import com.nero.hua.enumeration.PlayCardEnumeration;
    import com.nero.hua.enumeration.PlayCardTypeEnumeration;
    import com.nero.hua.enumeration.RobLandlordEnumeration;
    import com.nero.hua.exception.PlayCardException;
    import com.nero.hua.exception.RobLandlordException;
    import com.nero.hua.model.user.*;
    import com.nero.hua.util.CardUtil;
    import lombok.Getter;
    import lombok.Setter;
    import org.springframework.util.CollectionUtils;
    
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    
    @Getter
    @Setter
    public class GameManager {
    
        private static final int COLOR_CARD_COUNT = 52;
    
        private static final int NORMAL_USER_CARD_COUNT = 17;
    
        private static final int LANDLORD_CARD_COUNT = 3;
    
        private static final int MAX_USER_COUNT = 3;
    
        private List<CardEnumeration> landlordCardList;
    
        private RobLandlordRoundMO robLandlordRoundMO;
    
        private PlayCardRoundMO playCardRoundMO;
    
        public int getMaxUserCount() {
            return MAX_USER_COUNT;
        }
    
        public int getNormalUserCardCount() {
            return NORMAL_USER_CARD_COUNT;
        }
    
        public int getLandlordCardCount() {
            return LANDLORD_CARD_COUNT;
        }
    
        public boolean shouldStartGame(List<GameUserMO> gameUserMOList) {
            if (gameUserMOList.size() < MAX_USER_COUNT) {
                return Boolean.FALSE;
            }
    
            for (GameUserMO gameUserMO : gameUserMOList) {
                if (!gameUserMO.isPrepared()) {
                    return Boolean.FALSE;
                }
            }
    
            return Boolean.TRUE;
        }
    
        public boolean shouldNotStartGame(List<GameUserMO> gameUserMOList) {
            return !this.shouldStartGame(gameUserMOList);
        }
    
        public void startGame(List<GameUserMO> gameUserMOList) {
            List<CardEnumeration> shuffledCardList = this.shuffleCard();
            List<List<CardEnumeration>> dealCardList = this.dealCard(shuffledCardList);
            for (int i = 0; i < gameUserMOList.size(); i++) {
                Map<CardEnumeration, Integer> cardMap = CardUtil.convertCardListToCardMap(dealCardList.remove(0));
                gameUserMOList.get(i).setCardMap(cardMap);
            }
    
            this.landlordCardList = dealCardList.get(dealCardList.size() - 1);
        }
    
        private List<CardEnumeration> shuffleCard() {
            List<CardEnumeration> aDeckCardList = getADeckCardList();
            List<CardEnumeration> aShuffledCardList = new LinkedList<>();
            while (!aDeckCardList.isEmpty()) {
                int random = (int) (Math.random() * aDeckCardList.size());
                CardEnumeration randomCard = aDeckCardList.remove(random);
                aShuffledCardList.add(randomCard);
            }
            return aShuffledCardList;
        }
    
        private List<CardEnumeration> getADeckCardList() {
            List<CardEnumeration> cardEnumerationList = new LinkedList<>();
            CardEnumeration[] cardEnumerationArray = CardEnumeration.values();
            for (int i = 0; i < COLOR_CARD_COUNT; i++) {
                cardEnumerationList.add(cardEnumerationArray[i]);
            }
            cardEnumerationList.add(CardEnumeration.CARD_516);
            cardEnumerationList.add(CardEnumeration.CARD_517);
    
            return cardEnumerationList;
        }
    
        private List<List<CardEnumeration>> dealCard(List<CardEnumeration> shuffledCardList) {
            List<List<CardEnumeration>> dealCardList = new LinkedList<>();
    
            for (int i = 0; i < MAX_USER_COUNT; i++) {
                List<CardEnumeration> userCardList = new LinkedList<>();
                for (int j = 0; j < NORMAL_USER_CARD_COUNT; j++) {
                    userCardList.add(shuffledCardList.remove(0));
                }
                dealCardList.add(userCardList);
            }
    
            dealCardList.add(shuffledCardList);
    
            return dealCardList;
        }
    
        public String chooseOneUserToRobLandlord(List<GameUserMO> gameUserMOList) {
            int random  = (int) (Math.random() * MAX_USER_COUNT);
            String userId = gameUserMOList.get(random).getUserId();
            this.robLandlordRoundMO = new RobLandlordRoundMO(random, userId);
            return userId;
        }
    
        public void doRob(String userId) {
            UserRobLandlordTurnMO userRobLandlordTurnMO = this.thisGuyTurnForRobRound(userId);
            userRobLandlordTurnMO.setDoRob(Boolean.TRUE);
        }
    
        public void doNotRob(String userId) {
            UserRobLandlordTurnMO userRobLandlordTurnMO = this.thisGuyTurnForRobRound(userId);
            userRobLandlordTurnMO.setDoRob(Boolean.FALSE);
        }
    
        private UserRobLandlordTurnMO thisGuyTurnForRobRound(String userId) {
            if (null == this.robLandlordRoundMO) {
                throw new RobLandlordException(RobLandlordEnumeration.NOT_TIME_TO_ROB);
            }
    
            List<UserRobLandlordTurnMO> userRobLandlordTurnMOList = this.robLandlordRoundMO.getUserRobLandlordTurnMOList();
            UserRobLandlordTurnMO userRobLandlordTurnMO = userRobLandlordTurnMOList.get(userRobLandlordTurnMOList.size() - 1);
            if (!userId.equals(userRobLandlordTurnMO.getUserId())) {
                throw new RobLandlordException(RobLandlordEnumeration.NOT_YOUR_TURN);
            }
    
            return userRobLandlordTurnMO;
        }
    
        private int getUserIndexInUserListByUserId(String userId, List<GameUserMO> gameUserMOList) {
            for (int i = 0; i < gameUserMOList.size(); i++) {
                if (userId.equals(gameUserMOList.get(i).getUserId())) {
                    return i;
                }
            }
            return 0;
        }
    
        private GameUserMO getUserInUserListByUserId(String userId, List<GameUserMO> gameUserMOList) {
            for (int i = 0; i < gameUserMOList.size(); i++) {
                if (userId.equals(gameUserMOList.get(i).getUserId())) {
                    return gameUserMOList.get(i);
                }
            }
            return null;
        }
    
        public void giveLandlordCardToThisGuy(String userId, List<GameUserMO> gameUserMOList) {
            int userIndex = this.getUserIndexInUserListByUserId(userId, gameUserMOList);
    
            GameUserMO gameUserMO = gameUserMOList.get(userIndex);
    
            Map<CardEnumeration, Integer> cardMap = gameUserMO.getCardMap();
            for (CardEnumeration cardEnumeration : this.landlordCardList) {
                if (cardMap.containsKey(cardEnumeration)) {
                    Integer count = cardMap.get(cardEnumeration);
                    cardMap.put(cardEnumeration, count + 1);
                }
                else {
                    cardMap.put(cardEnumeration, 1);
                }
            }
    
            this.playCardRoundMO = new PlayCardRoundMO();
            this.playCardRoundMO.addNewUserToStartPlayCard(userIndex, userId);
        }
    
        public boolean hasNextOneToStartRob() {
            if (null == this.robLandlordRoundMO) {
                throw new RobLandlordException(RobLandlordEnumeration.NOT_TIME_TO_ROB);
            }
    
            return this.robLandlordRoundMO.getUserRobLandlordTurnMOList().size() < MAX_USER_COUNT - 1;
        }
    
        public String makeNextUserToStartRob(List<GameUserMO> gameUserMOList) {
            int index = robLandlordRoundMO.getCurrentTurnUserIndex();
    
            int nextIndex = (index + 1) % this.getMaxUserCount();
            String nextUserId = gameUserMOList.get(nextIndex).getUserId();
    
            this.robLandlordRoundMO.addNewUserToStartRob(nextIndex, nextUserId);
    
            return nextUserId;
        }
    
        public String makeLastUserRobLandlordCard(List<GameUserMO> gameUserMOList) {
            int index = robLandlordRoundMO.getCurrentTurnUserIndex();
    
            int nextIndex = (index + 1) % this.getMaxUserCount();
            String nextUserId = gameUserMOList.get(nextIndex).getUserId();
    
            this.robLandlordRoundMO.addNewUserToDoRob(nextIndex, nextUserId);
    
            return nextUserId;
        }
    
        public void doPlayCard(String userId, List<CardEnumeration> cardEnumerationList, PlayCardTypeEnumeration playCardTypeEnumeration, List<GameUserMO> gameUserMOList) {
            if (CardUtil.playCardNotMatchPlayCardType(cardEnumerationList, playCardTypeEnumeration)) {
                throw new PlayCardException(PlayCardEnumeration.PLAY_CARD_DO_NOT_MATCH_ITS_TYPE);
            }
    
            UserPlayCardTurnMO userPlayCardTurnMO = this.thisGuyTurnForPlayCardRound(userId);
    
            GameUserMO gameUserMO = gameUserMOList.get(userPlayCardTurnMO.getUserIndex());
            Map<CardEnumeration, Integer> cardEnumerationMap = CardUtil.convertCardListToCardMap(cardEnumerationList);
            if (CardUtil.handCardMapNotContainsPlayCardMap(gameUserMO.getCardMap(), cardEnumerationMap)) {
                throw new PlayCardException(PlayCardEnumeration.HAND_CARD_DO_NOT_CONTAINS_PLAY_CARD);
            }
    
            UserPlayCardTurnMO lastUserPlayCardTurnMO = this.playCardRoundMO.getLastUserPlayCardTurnMO();
            if (CardUtil.currentPlayCardListNotBetterThanLastPlayCardList(lastUserPlayCardTurnMO, cardEnumerationList, playCardTypeEnumeration)) {
               throw new PlayCardException(PlayCardEnumeration.PLAY_CARD_DO_NOT_BETTER_THAN_LAST_PLAY_CARD);
            }
    
            this.removeUserCardList(gameUserMO, cardEnumerationList);
    
            userPlayCardTurnMO.setCardList(cardEnumerationList);
            userPlayCardTurnMO.setPlayCardTypeEnumeration(playCardTypeEnumeration);
        }
    
        private UserPlayCardTurnMO thisGuyTurnForPlayCardRound(String userId) {
            if (null == this.playCardRoundMO) {
                throw new PlayCardException(PlayCardEnumeration.NOT_TIME_TO_PLAY_CARD);
            }
    
            List<UserPlayCardTurnMO> userPlayCardTurnMOList = this.playCardRoundMO.getUserPlayCardTurnMOList();
            UserPlayCardTurnMO userPlayCardTurnMO = userPlayCardTurnMOList.get(userPlayCardTurnMOList.size() - 1);
            if (!userId.equals(userPlayCardTurnMO.getUserId())) {
                throw new PlayCardException(PlayCardEnumeration.NOT_YOUR_TURN);
            }
    
            return userPlayCardTurnMO;
        }
    
        private void removeUserCardList(GameUserMO gameUserMO, List<CardEnumeration> cardEnumerationList) {
            Map<CardEnumeration, Integer> cardMap = gameUserMO.getCardMap();
            for (CardEnumeration cardEnumeration : cardEnumerationList) {
                Integer cardCount = cardMap.get(cardEnumeration);
                if (1 == cardCount) {
                    cardMap.remove(cardEnumeration);
                }
                else {
                    cardMap.put(cardEnumeration, cardCount - 1);
                }
            }
        }
    
        public boolean thisGuyWin(String userId, List<GameUserMO> gameUserMOList) {
            GameUserMO gameUserMO = this.getUserInUserListByUserId(userId, gameUserMOList);
    
            return CollectionUtils.isEmpty(gameUserMO.getCardMap());
        }
    
        public String makeNextUserToStartPlayCard(List<GameUserMO> gameUserMOList) {
            int index = playCardRoundMO.getCurrentTurnUserIndex();
    
            int nextIndex = (index + 1) % this.getMaxUserCount();
            String nextUserId = gameUserMOList.get(nextIndex).getUserId();
    
            this.playCardRoundMO.addNewUserToStartPlayCard(nextIndex, nextUserId);
    
            return nextUserId;
        }
    
        public void doNotPlayCard(String userId) {
            this.thisGuyTurnForPlayCardRound(userId);
    
            UserPlayCardTurnMO lastUserPlayCardTurnMO = this.playCardRoundMO.getLastUserPlayCardTurnMO();
            if (null == lastUserPlayCardTurnMO) {
                throw new PlayCardException(PlayCardEnumeration.MUST_PLAY_CARD_WHEN_ROUND_START);
            }
        }
    
        public boolean hasNextOneToStartPlayCard() {
            return !this.playCardRoundMO.thisRoundFinish(this.getMaxUserCount());
        }
    
        public String makeLastPlayCardUserToStartPlayCard() {
            List<UserPlayCardTurnMO> userPlayCardTurnMOList = playCardRoundMO.getUserPlayCardTurnMOList();
            UserPlayCardTurnMO lastUserPlayCardTurnMO = userPlayCardTurnMOList.get(userPlayCardTurnMOList.size() - 1 - (MAX_USER_COUNT - 1));
    
            this.playCardRoundMO = new PlayCardRoundMO();
            this.playCardRoundMO.addNewUserToStartPlayCard(lastUserPlayCardTurnMO.getUserIndex(), lastUserPlayCardTurnMO.getUserId());
    
            return lastUserPlayCardTurnMO.getUserId();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    package com.nero.hua.model.user;
    
    import lombok.Getter;
    import lombok.Setter;
    import org.springframework.util.CollectionUtils;
    
    import java.util.LinkedList;
    import java.util.List;
    
    @Getter
    @Setter
    public class PlayCardRoundMO {
    
        private int currentTurnUserIndex;
    
        List<UserPlayCardTurnMO> userPlayCardTurnMOList = new LinkedList<>();
    
        public void addNewUserToStartPlayCard(int userIndex, String userId) {
            this.currentTurnUserIndex = userIndex;
            userPlayCardTurnMOList.add(new UserPlayCardTurnMO(userIndex, userId));
        }
    
        public boolean thisRoundFinish(int maxUserCount) {
            if (this.userPlayCardTurnMOList.size() < maxUserCount) {
                return Boolean.FALSE;
            }
    
            for (int i = 0; i < maxUserCount - 1; i++) {
                if (this.userPlayCardTurnMOList.get(this.userPlayCardTurnMOList.size() - 1 - i).userDoPlayCard()) {
                    return Boolean.FALSE;
                }
            }
            return Boolean.TRUE;
        }
    
        public UserPlayCardTurnMO getLastUserPlayCardTurnMO() {
            if (CollectionUtils.isEmpty(userPlayCardTurnMOList)) {
                return null;
            }
    
            for (int i = this.userPlayCardTurnMOList.size() - 1; i >= 0; i--) {
                if (!CollectionUtils.isEmpty(this.userPlayCardTurnMOList.get(i).getCardList())) {
                    return this.userPlayCardTurnMOList.get(i);
                }
            }
    
            return null;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    前端0号座椅总是为自己的设计

    跟后端的回合流转类似,只不过多了位置。
    前端房间的userList跟后端的userList保持一直,通过一个计算座椅位置来达到目的

    import Card from "../../../enumeration/CardEnumeration";
    import PlayCardType from "../../../enumeration/PlayCardTypeEnumeration";
    
    export default class RoundMO {
    
      private currentTurnUserId : string ;
    
      private palyCardList : Array<Array<Card>> = new Array();
    
      private palyCardTypeList : Array<PlayCardType> = new Array();
    
      constructor(currentTurnUserId : string) {
        this.currentTurnUserId = currentTurnUserId;
      }
    
      public getCurrentTurnUserId() : string {
        return this.currentTurnUserId;
      }
    
      public setCurrentTurnUserId(currentTurnUserId : string) : void {
        this.currentTurnUserId = currentTurnUserId;
      }
    
      public doPlayCard(cardList : Array<Card>, playCardType : PlayCardType) : void {
        this.palyCardList.push(cardList);
        this.palyCardTypeList.push(playCardType);
      }
    
      public doNotPlayCard() : void {
        this.palyCardList.push(null);
        this.palyCardTypeList.push(null);
      }
    
      public thisRoundFinish(maxUserCount : number) : boolean {
        if (this.palyCardList.length < maxUserCount) {
          return false;
        }
    
        for (let i : number = 0, j : number = this.palyCardList.length - 1; i < maxUserCount - 1; i++, j--) {
          if (null !== this.palyCardList[j]) {
              return false;
          }
        }
    
        return true;
      }
    
      public getLastPlayCard() : Array<Card> {
        if (null == this.palyCardList) {
          return null;
        }
    
        for (let i = this.palyCardList.length; i >= 0; i--) {
          if (null != this.palyCardList[i]) {
            return this.palyCardList[i];
          }
        }
    
        return null;
      }
    
      public getLastPlayCardType() : PlayCardType {
        if (null == this.palyCardTypeList) {
          return null;
        }
    
        for (let i = this.palyCardTypeList.length; i >= 0; i--) {
          if (null != this.palyCardTypeList[i]) {
            return this.palyCardTypeList[i];
          }
        }
    
        return null;
      }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
        private calculateSeatIndexByMeIndex(userIndex : number, meIndex : number) : number {
          return (userIndex - meIndex + 3) % 3;
        }
    
    • 1
    • 2
    • 3

    效果图

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    MySQL学习笔记1
    【限时免费】20天拿下华为OD笔试之 【回溯】2023B-找到它【欧弟算法】全网注释最详细分类最全的华为OD真题题解
    想要精通算法和SQL的成长之路 - 受限条件下可到达节点的数目
    04)go语言使用优化 启动时不打开CMD控制台,后台运行
    WebGL 计算平行光、环境光下的漫反射光颜色
    C++数据结构X篇_04_单向链表框架搭建、实现和测试(链表的定义,常用操作的实现等)
    秋招突击——算法打卡——6/5——提高{(状态机模型)股票买卖、(单调队列优化DP)最大子序列和}——新做:{考试的最大困扰度}
    MyEclipse项目导入与导出
    django —— 搭建项目及简单测试
    还清90万安居贷才能拿到离职证明?腾讯回应:与实际情况不符
  • 原文地址:https://blog.csdn.net/weixin_45043548/article/details/126269102