. Net Core - play a game with SignalR

Before the internal training, when it comes to real-time web applications, I talked about SignalR. I said to make a game with it sometime, but I haven't arranged it since the time was tight. I think of this thing again when I'm free these two days. After considering it, I decided to write a Dou D Master in two days. I arranged front-end students to write the client, and I wrote game logic and services.

The difficulty of this project is not high, but the game logic is still very winding, and many small problems have been found and solved in the process of joint commissioning. Come and organize an article in the garden and record it.

The basic introduction is unnecessary. After all, I understand everything on the official website after two rounds. No foundation can Poke here , is a basic introduction to SignalR I wrote before, with a minimalist chat room.

tips: there is an open source address at the end of the article. The game data is local. You can play it by changing the IP.

The first is the data model:

    /// <summary>
    /// User information
    /// </summary>
    public class Customer
    {
        /// <summary>
        /// only ID
        /// </summary>
        public string? ID { get; set; }

        /// <summary>
        /// nickname
        /// </summary>
        public string? NickName { get; set; }

        /// <summary>
        /// card
        /// </summary>
        public List<string> Card { get; set; }
    }


    /// <summary>
    /// room
    /// </summary>
    public class Room
    {
        /// <summary>
        /// Room name
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Homeowner id
        /// </summary>
        public string Masterid { get; set; }

        /// <summary>
        /// Current player
        /// </summary>
        public int Curr { get; set; }

        /// <summary>
        /// Current card
        /// </summary>
        public List<string> CurrCard { get; set; } = new List<string>();

        /// <summary>
        /// Current card printer
        /// </summary>
        public string ExistingCardClient { get; set; }

        /// <summary>
        /// Room member list
        /// </summary>
        public List<Customer> Customers { get; set; } = new List<Customer>();
    }

tips: it's just designed for fighting D. the commercial version can't do that. Please use it with caution for reference.

With the data model, CRUD is indispensable:

    /// <summary>
    /// User operation
    /// </summary>
    public static class CustomerAction
    {
        /// <summary>
        /// User list
        /// </summary>
        private static List<Customer> cusList = new List<Customer>();

        /// <summary>
        /// If it does not exist, it will be added; if it exists, it will be modified
        /// </summary>
        /// <param name="customer"></param>
        public static void Create(Customer customer)
        {
            Customer curr = null;

            if (cusList.Count > 0)
                curr = cusList.Where(x => x.ID == customer.ID).FirstOrDefault();

            if (curr is null)
                cusList.Add(customer);
            else
            {
                curr.NickName = customer.NickName;

                Up4ID(curr);
            }
        }

        /// <summary>
        /// User list
        /// </summary>
        /// <returns></returns>
        public static List<Customer> GetList()
        {
            return cusList;
        }

        /// <summary>
        /// Get single
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static Customer GetOne(string id)
        {
            return cusList.Where(x => x.ID == id).FirstOrDefault();
        }

        /// <summary>
        /// delete user
        /// </summary>
        /// <param name="id"></param>
        public static void Delete(string id)
        {
            cusList.RemoveAll(x => x.ID == id);
        }

        /// <summary>
        /// Add card
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cards"></param>
        public static void InCard(string id, List<string> cards)
        {
            Customer customer = cusList.Where(x => x.ID == id).FirstOrDefault();

            if (customer.Card is null)
                customer.Card = cards;
            else
                customer.Card.AddRange(cards);

            Up4ID(customer);
        }

        /// <summary>
        /// Deduction card
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cards"></param>
        /// <param name="group"></param>
        /// <returns></returns>
        public static bool OutCard(string id, List<string> cards, Room group)
        {
            Customer client = cusList.Where(x => x.ID == id).FirstOrDefault();

            if (client is null)
                return false;

            //Card mismatch direct failure
            if (client.Card.Where(x => cards.Contains(x)).ToList().Count != cards.Count)
                return false;

            //Direct failure if it does not comply with the licensing rules
            if (!new Game.WithCard().Rule(group.CurrCard, cards, group.ExistingCardClient is null || group.ExistingCardClient == id))
                return false;

            foreach (var item in cards)
            {
                client.Card.Remove(item);
            }

            group.CurrCard = cards;

            group.ExistingCardClient = id;

            Up4ID(client);

            RoomAction.Up4Name(group);

            return true;
        }

        /// <summary>
        /// Update (as per ID)
        /// </summary>
        /// <param name="customer"></param>
        /// <returns></returns>
        public static bool Up4ID(Customer customer)
        {
            if (cusList.Count == 0)
                return false;

            cusList.RemoveAll(x => x.ID == customer.ID);

            cusList.Add(customer);

            return true;
        }
    }


    /// <summary>
    /// Room operation
    /// </summary>
    public static class RoomAction
    {
        /// <summary>
        /// Room list
        /// </summary>
        private static List<Room> roomList = new List<Room>();

        /// <summary>
        /// New room
        /// If the room already exists, it will not be added
        /// </summary>
        /// <param name="group"></param>
        public static void Create(Room group)
        {
            if (!roomList.Where(x => x.Name == group.Name).Any())
                roomList.Add(group);
        }

        /// <summary>
        /// Get list
        /// </summary>
        /// <returns></returns>
        public static List<Room> GetList()
        {
            return roomList;
        }

        /// <summary>
        /// Get single
        /// </summary>
        /// <param name="masterid">Homeowner id</param>
        /// <param name="roomName">Room name</param>
        /// <returns></returns>
        public static Room GetOne(string masterid = null, string roomName = null)
        {
            if (roomList.Count == 0)
                return null;

            if (masterid != null)
                return roomList.Where(x => x.Masterid == masterid).FirstOrDefault();

            if (roomName != null)
                return roomList.Where(x => x.Name == roomName).FirstOrDefault();

            return null;
        }

        /// <summary>
        /// Join the room
        /// </summary>
        /// <param name="client"></param>
        /// <param name="roomName"></param>
        public static bool Join(Customer client, string roomName)
        {
            if (roomList.Count == 0)
                return false;

            var room = roomList.Where(x => x.Name == roomName).FirstOrDefault();

            if (room is null)
                return false;

            if (room.Customers.Count == 3)
                return false;

            room.Customers.Add(client);

            Up4Name(room);

            return true;
        }

        /// <summary>
        /// Delete room
        /// </summary>
        /// <param name="masterid">Homeowner id</param>
        public static bool Delete(string masterid)
        {
            if (roomList.Count == 0)
                return false;

            var room = roomList.Where(x => x.Masterid == masterid).FirstOrDefault();

            if (room == null)
                return false;

            roomList.Remove(room);

            return true;
        }

        /// <summary>
        /// Update (by room name)
        /// </summary>
        /// <param name="room"></param>
        /// <returns></returns>
        public static bool Up4Name(Room room)
        {
            if (roomList.Count == 0)
                return false;

            roomList.RemoveAll(x => x.Name == room.Name);

            roomList.Add(room);

            return true;
        }

        /// <summary>
        /// Update current player
        /// </summary>
        /// <param name="roomName"></param>
        /// <param name="index">If it is passed in, it is forced to be modified. If it is not passed in, follow the rules</param>
        public static Customer ChangeCurr(string roomName, int index = -1)
        {
            var room = roomList.Where(x => x.Name == roomName).FirstOrDefault();

            if (index != -1)
                room.Curr = index;
            else
                room.Curr = (room.Curr + 1) % 3;

            Up4Name(room);

            return room.Customers[room.Curr];
        }
    }

Because all data is saved through static attributes, most of them are linq operations (forgive me for the limited level of linq).

Next is the game logic:

    /// <summary>
    /// Card related
    /// </summary>
    public class WithCard
    {
        /// <summary>
        /// spade-S,heart-H,Plum blossom-C,block-D
        /// BG King, SG Xiao Wang, 14-A,15-2
        /// </summary>
        readonly List<string> Cards = new List<string>()
        {
            "S-14","S-15","S-3","S-4","S-5","S-6","S-7","S-8","S-9","S-10","S-11","S-12","S-13",
            "H-14","H-15","H-3","H-4","H-5","H-6","H-7","H-8","H-9","H-10","H-11","H-12","H-13",
            "C-14","C-15","C-3","C-4","C-5","C-6","C-7","C-8","C-9","C-10","C-11","C-12","C-13",
            "D-14","D-15","D-3","D-4","D-5","D-6","D-7","D-8","D-9","D-10","D-11","D-12","D-13",
            "BG-99","SG-88"
        };

        /// <summary>
        /// Licensing
        /// </summary>
        public List<List<string>> DrawCard()
        {
            List<string> a = new List<string>();
            List<string> b = new List<string>();
            List<string> c = new List<string>();

            Random ran = new Random();

            //There are three cards left
            for (int i = 0; i < 51; i++)
            {
                //Draw a card at random
                string item = Cards[ran.Next(Cards.Count)];

                switch (i % 3)
                {
                    case 0:
                        a.Add(item);
                        break;
                    case 1:
                        b.Add(item);
                        break;
                    case 2:
                        c.Add(item);
                        break;
                }

                Cards.Remove(item);
            }

            return new List<List<string>>()
            {
                a,b,c,Cards
            };
        }

        /// <summary>
        /// rule
        /// </summary>
        /// <param name="existingCard"></param>
        /// <param name="newCard"></param>
        /// <param name="isSelf"></param>
        /// <returns></returns>
        public bool Rule(List<string> existingCard, List<string> newCard, bool isSelf)
        {
            //Existing brand
            List<int> existingCardNo = existingCard.Select(x => Convert.ToInt32(x.Split('-')[1])).ToList().OrderBy(x => x).ToList();

            //New card number
            List<int> newCardNo = newCard.Select(x => Convert.ToInt32(x.Split('-')[1])).ToList().OrderBy(x => x).ToList();

            //The first hand is Wang fried. No one else is allowed to play cards
            if (existingCardNo.All(x => x > 50) && existingCardNo.Count == 2)
            {
                if (isSelf)
                    return true;
                else
                    return false;
            }

            //Wang fried the largest
            if (newCardNo.All(x => x > 50) && newCard.Count == 2)
                return true;

            //Leaflet
            if (newCardNo.Count == 1)
            {
                if (existingCardNo.Count == 0)
                    return true;

                if ((existingCardNo.Count == 1 && newCardNo[0] > existingCardNo[0]) || isSelf)
                    return true;
            }

            //Pair/Three
            if (newCardNo.Count == 2 || newCardNo.Count == 3)
            {
                if (existingCardNo.Count == 0 && newCardNo.All(x => x == newCardNo[0]))
                    return true;

                if (newCardNo.All(x => x == newCardNo[0]) && (isSelf || newCardNo.Count == existingCardNo.Count && newCardNo[0] > existingCardNo[0]))
                    return true;
            }

            if (newCard.Count == 4)
            {
                //Blow up
                if (newCardNo.All(x => x == newCardNo[0]))
                {
                    if (existingCardNo.Count == 0 || isSelf)
                        return true;

                    if (existingCardNo.All(x => x == existingCardNo[0]) && existingCardNo.Count == 4)
                    {
                        if (newCardNo[0] > existingCardNo[0])
                            return true;
                    }

                    return true;
                }

                //Three with one
                {
                    List<int> flagA = newCardNo.Distinct().ToList();

                    //Direct failure of more than 2 cards
                    if (flagA.Count > 2)
                        return false;

                    //There is no previous hand, or the previous hand is your own card
                    if (existingCardNo.Count == 0 || isSelf)
                        return true;

                    int newCardFlag = 0;

                    if (newCardNo.Where(x => x == flagA[0]).ToList().Count() > 1)
                    {
                        newCardFlag = flagA[0];
                    }
                    else
                        newCardFlag = flagA[1];

                    List<int> flagB = existingCardNo.Distinct().ToList();

                    //The last hand was not three with one
                    if (flagB.Count > 2)
                        return false;

                    int existingCardFlag = 0;

                    if (existingCardNo.Where(x => x == flagB[0]).ToList().Count() > 1)
                    {
                        existingCardFlag = flagB[0];
                    }
                    else
                        existingCardFlag = flagB[1];

                    if (newCardFlag > existingCardFlag)
                        return true;
                }
            }

            if (newCard.Count >= 5)
            {
                bool flag = true;

                for (int i = 0; i < newCardNo.Count - 1; i++)
                {
                    if (newCardNo[i] + 1 != newCardNo[i + 1])
                    {
                        flag = false;
                        break;
                    }
                }

                //Shunzi
                if (flag)
                {
                    if (existingCardNo.Count == 0 || (newCardNo[0] > existingCardNo[0] && newCardNo.Count == existingCardNo.Count) || isSelf)
                        return true;
                }
            }

            return false;
        }
    }

The single rule is the same as that of the ordinary doud master (except Wang, 2 are the largest, followed by A). At present, multiple rules support: Wang fried, pair, three, shunzi, three with one. At present, it is only here. You can take it back and expand it yourself.

The owner built the house and added:

New players join:

When the room is full, the owner starts the game and randomly assigns the landlord:

Licensing effects:

Game settlement:

Finally, attach the open source address (the client is in the web branch): https://gitee.com/muchengqingxin/card-game

tips: if the front-end students do this without UI cooperation, they must give a praise. Finally, I remind you not to take it for commercial use.

Posted on Sun, 05 Dec 2021 06:28:15 -0500 by ziegel