.Net Core——用SignalR撸个游戏


之前开内部培训,说到实时web应用这一块讲到了SignalR,我说找时间用它做个游戏玩玩,后面时间紧张就一直没安排。这两天闲了又想起这个事,考虑后决定用2天时间写个斗D主,安排了前端同学写客户端,我写游戏逻辑和服务。

这个项目难度并不高,但是游戏逻辑还是挺绕的,联调过程中也发现解决了很多小问题。来园子里整理一篇文章,记录一下。

基础的介绍就免了,毕竟官网跟着走两圈啥都懂了。没基础的可以戳这里,是我之前写的一篇SignalR基础介绍,带有一个极简聊天室。

tips:文章结尾有开源地址,游戏数据都是本地的,改下IP运行起来就可以玩了。

直接上干货,首先是数据模型:

    /// <summary>
    /// 用户信息
    /// </summary>
    public class Customer
    {
        /// <summary>
        /// 唯一ID
        /// </summary>
        public string? ID { get; set; }

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

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


    /// <summary>
    /// 房间
    /// </summary>
    public class Room
    {
        /// <summary>
        /// 房间名
        /// </summary>
        public string Name { get; set; }

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

        /// <summary>
        /// 当前出牌人
        /// </summary>
        public int Curr { get; set; }

        /// <summary>
        /// 当前卡片
        /// </summary>
        public List<string> CurrCard { get; set; } = new List<string>();

        /// <summary>
        /// 当前卡片打出人
        /// </summary>
        public string ExistingCardClient { get; set; }

        /// <summary>
        /// 房间成员列表
        /// </summary>
        public List<Customer> Customers { get; set; } = new List<Customer>();
    }

tips:只是单纯为了斗D主设计的,商用版肯定不能这么搞,参考请慎用。

有了数据模型,自然少不了CRUD:

    /// <summary>
    /// 用户操作
    /// </summary>
    public static class CustomerAction
    {
        /// <summary>
        /// 用户列表
        /// </summary>
        private static List<Customer> cusList = new List<Customer>();

        /// <summary>
        /// 不存在则新增,存在则修改昵称
        /// </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>
        /// 用户列表
        /// </summary>
        /// <returns></returns>
        public static List<Customer> GetList()
        {
            return cusList;
        }

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

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

        /// <summary>
        /// 增加卡片
        /// </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>
        /// 扣除卡片
        /// </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;

            //卡片不匹配直接失败
            if (client.Card.Where(x => cards.Contains(x)).ToList().Count != cards.Count)
                return false;

            //不符合出牌规则直接失败
            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>
        /// 更新(根据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>
    /// 房间操作
    /// </summary>
    public static class RoomAction
    {
        /// <summary>
        /// 房间列表
        /// </summary>
        private static List<Room> roomList = new List<Room>();

        /// <summary>
        /// 新增房间
        /// 如果房间已存在则不新增
        /// </summary>
        /// <param name="group"></param>
        public static void Create(Room group)
        {
            if (!roomList.Where(x => x.Name == group.Name).Any())
                roomList.Add(group);
        }

        /// <summary>
        /// 获取列表
        /// </summary>
        /// <returns></returns>
        public static List<Room> GetList()
        {
            return roomList;
        }

        /// <summary>
        /// 获取单个
        /// </summary>
        /// <param name="masterid">房主id</param>
        /// <param name="roomName">房间名称</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>
        /// 加入房间
        /// </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>
        /// 删除房间
        /// </summary>
        /// <param name="masterid">房主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>
        /// 更新(根据房名)
        /// </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>
        /// 更新当前出牌人
        /// </summary>
        /// <param name="roomName"></param>
        /// <param name="index">传入则强制修改,不传按规则走</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];
        }
    }

因为所有数据都是通过静态属性保存的,所以大部分都是linq操作(原谅我linq水平有限)。

接下来是游戏逻辑:

    /// <summary>
    /// 卡片相关
    /// </summary>
    public class WithCard
    {
        /// <summary>
        /// 黑桃-S、红桃-H、梅花-C、方块-D
        /// BG大王,SG小王,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>
        /// 发牌
        /// </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();

            //剩3张底牌
            for (int i = 0; i < 51; i++)
            {
                //随机抽取一张牌
                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>
        /// 规则
        /// </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)
        {
            //现有牌号
            List<int> existingCardNo = existingCard.Select(x => Convert.ToInt32(x.Split('-')[1])).ToList().OrderBy(x => x).ToList();

            //新出牌号
            List<int> newCardNo = newCard.Select(x => Convert.ToInt32(x.Split('-')[1])).ToList().OrderBy(x => x).ToList();

            //上一手是王炸,禁止其他人出牌
            if (existingCardNo.All(x => x > 50) && existingCardNo.Count == 2)
            {
                if (isSelf)
                    return true;
                else
                    return false;
            }

            //王炸最大
            if (newCardNo.All(x => x > 50) && newCard.Count == 2)
                return true;

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

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

            //对子/三只
            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)
            {
                //
                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;
                }

                //三带一
                {
                    List<int> flagA = newCardNo.Distinct().ToList();

                    //超过2种牌直接失败
                    if (flagA.Count > 2)
                        return false;

                    //没有上一手牌,或者上一手是自己出的牌
                    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();

                    //上一手牌不是三带一
                    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;
                    }
                }

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

            return false;
        }
    }

单张规则和普通斗D主一样(除了王以外2最大,其次是A),多张规则目前支持:王炸、对子、三只、顺子、三带一。目前只做到这里,各位同学可以拿回去自行扩展。

上一些运行图。房主建房并加入:

新玩家加入:

房间人满以后房主开始游戏,随机分配地主:

出牌特效:

游戏结算:

最后附上开源地址(客户端在web分支):https://gitee.com/muchengqingxin/card-game

tips:前端同学在没有UI配合的情况下做到现在这样,必须给个赞。最后提醒大家,不要拿去商用。

文章来源:https://www.cnblogs.com/muchengqingxin/p/15612678.html

版权声明:本文为YES开发框架网发布内容,转载请附上原文出处连接
管理员
上一篇:附加属性的使用
下一篇:也谈string.Join和StringBuilder的性能比较
评论列表

发表评论

评论内容
昵称:
验证码:
验证码
关联文章

.Net Core——SignalR游戏
.NET Core ResponseCache 浏览器缓存
ASP.NET Core官网教程,资料查找
.NET Core 缓存使用之 MemoryCache
.NET Core 自定义中间件 Middleware
.NET Core定时任务(控制台程序)
.net core使用Microsoft.Data.Sqlite创建SQLite数据库文件
.NET Core,.NET5 固定输出目录,不要版本目录
.net core 获得路径
ASP.NET Core MVC中的路由约束
asp.net core 支持多种身份认证方式
.NET Core 实现动态代理做AOP(面向切面编程)
.NET Core 利用委托实现动态流程组装
.net core如何添加一个唯一的请求ID
网站分享
ASP.NET Core 使用 LESS
.NET Core集成JWT授权验证
AP.NET Core获得注入管理器
化繁为简,例子介绍JavaScript异步处理async awite
.NET5 ASP.NET Core 添加API限流

热门标签
.NET Core .NET Reactor ag-grid AI发布 api安全 ASP.NET Core C#DLL加密 C#播放声音 C#代码混淆 C#代码加密 ChromeDriver Codex DateTime DBeaver devexpress devTool DLL混淆 edge.js EF EFCore Electron element-ui el-form el-table excel FastReport FileStream FolderBrowerDialog FolderSelectDialog form提交 git gridcontrol gridview input javascript json字符串 JS转换对象JSON jwt JWT授权 linq log Math MCP mitmproxy MVC MySQL Navicat netstat nginx node_modules NSwag Nuget Nuget镜像 number PowerShell pyinstaller python pythoncom python爬虫 python抓包 pywin32 redis Requests-html RestSharp Selenium sql SQL Server Swagger to-cms Visual Studio VSCode vue VueRouter vue路由 VUE页面通讯 Webpack Windows Windows服务 winform wmi xlrd yaml YESCMS YESWEB开发框架 白象 表单提交 播放声音 打开URL 代码混淆 弹窗提醒 端口占用 对象转换 分布式 公共字典 机器码 进程排查 静态资源 开发指南 路由参数 密钥 配置教程 配置文件 权限 人工智能 任务 任务调度 日期间隔 日志 日志记录 省市区 授权验证 数据库 四舍五入 文案 文件读取 文件夹选择 文件目录选择 问题排查 行政区域数据 页面通讯 中间件 CSharp 事务锁 工单系统 并发控制 重复提交 CMS Markdig Markdown markdown-it marked 技术选型 VS Code 开发工具 源代码管理 版本控制 Docker PostgreSQL 时区 部署排查 CMS架构 EF Core 主题系统 二次开发 插件系统 容器 运维命令 镜像清理 Linux NAS 远程挂载 飞牛 fnOS S/4HANA SAP GUI SAP HANA SAP R/3 SAP入门 SAP版本 ERP SAP SAP MM 库存管理 物料管理 采购管理 入门教程 SAP S/4HANA SPRO 企业结构 采购组织 MM01 物料主数据 物料类型 BP分组 业务伙伴 供应商主数据 ME41 RFQ 库存物料 采购流程 ME51 消耗性物料 科目分配 采购申请 AC03 ML81N 外部服务 服务主数据 Business Partner SAP培训 ME51N MM模块 Lean Services MM-SRV 外部服务采购 PIR 供应来源 采购主数据 采购信息记录 ME31K 框架协议 计划协议 采购合同 ME01 供应来源确定 货源清单 MEQ1 供应源确定 配额安排 配额评分 MD04 MD21 MRP 计划文件 需求计划 批量程序 MD01N MD02 MRP Live MD05 MM 物料计划 优化采购 供应源 采购订单 ME2A 供应商确认 采购监控 Flexible Workflow 凭证释放 采购审批 释放策略 实地盘点 物料凭证 货物移动 MIGO 收货 移动类型 已撤回 供应商退货 货物发出 STO 库存转储 转移过账 生产订单 预留 GR/IR MIRO 供应商发票 物流发票校验 OMR2 税码 FI PP SD 实操教程 MRBR OMR6 发票差异 交货成本 后续借记 MI01 实物盘点 盘点差异 公司代码 工厂 组织结构 OMS2 主数据定制 自动科目确定 BP角色 CVI 伙伴确定 编号范围 凭证类型 字段选择 FBN1 OMBT OMC2 会计凭证 OMJJ BOM 委外加工 项目类别L MRKO 供应商寄售 特殊库存K MRKON PIPE Pipeline 特殊库存P ERS MRIS 发票计划 周期性结算 里程碑付款 变更追踪 版本管理 采购凭证 SFTP WebDAV 网盘 飞牛fnOS AMPL HERS MPN 中文教程 库存确定 可用性检查 缺件检查 Output Management 消息确定 输出确定 分割评估 库存计价 评估类别 评估类型 PB00 RM0000 条件技术 采购定价 MM-FI集成 OBYC 库存估价 文本类型 文本采用 EFB EVO MSV SU3 用户参数 发票校验 合同参照 履约保留款 特别总账 预付款 Fiori Launchpad SAP Fiori 应用导航 用户体验 LSMW LTMC Migration Cockpit 数据迁移 BRFplus OPD Output Control My Inbox 审批流程 灵活工作流 SAP PP 外部加工 SAP QM 检验批 质量信息记录 采购收货 SAP PM 维护BOM 维护订单 SAP SD SAP Service 端到端流程 MM模块培训 FI-MM集成 供应商管理 审批配置 FICO入门 SAP FICO 财务配置 供应商税务 预扣税 House Bank 银行对账 客户清账 应收账款 FI控制 验证与替代 印度 GST 税务配置 F110 FBZP EWM入门 SAP EWM 仓库管理 OX14 成本核算 物料评估 后勤配置 物料组 价值更新 数量更新 PP-PI 流程制造 生产计划 容差配置 SAP事务码 SAP基础 TCODE Basis 事务代码 MMNR 编号区间 采购实操 组织架构 OMSF SAP实操 FI配置 端口修改 密码设置 数据库配置 远程访问 ABAP基础 SAP ABAP 内表 变量定义 常量 数据类型 ABAP SAP开发 变量 基础语法 系统变量 结构体 字符串处理 循环语句 控制语句 DDIC SE11 数据字典 透明表 ABAP开发 ALE EDI IDoc 增强技术 ABAP内表 HASHED TABLE SORTED TABLE STANDARD TABLE 基本概念 性能优化 PARAMETERS SELECT-OPTIONS SELECTION-SCREEN 报表程序 选择屏幕 F4帮助 Report事件 输入校验 ABAP SQL ABAP语法 Open SQL SELECT 数据库访问 IKuai IP修改 PVE 网络配置 虚拟化
联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码