.Net Core使用SignalR實(shí)現(xiàn)斗地主游戲
之前開內(nèi)部培訓(xùn),說(shuō)到實(shí)時(shí)web應(yīng)用這一塊講到了SignalR,我說(shuō)找時(shí)間用它做個(gè)游戲玩玩,后面時(shí)間緊張就一直沒安排。這兩天閑了又想起這個(gè)事,考慮后決定用2天時(shí)間寫個(gè)斗D主,安排了前端同學(xué)寫客戶端,我寫游戲邏輯和服務(wù)。
這個(gè)項(xiàng)目難度并不高,但是游戲邏輯還是挺繞的,聯(lián)調(diào)過(guò)程中也發(fā)現(xiàn)解決了很多小問(wèn)題。來(lái)園子里整理一篇文章,記錄一下。
基礎(chǔ)的介紹就免了,畢竟官網(wǎng)跟著走兩圈啥都懂了。沒基礎(chǔ)的可以戳這里,是我之前寫的一篇SignalR基礎(chǔ)介紹,帶有一個(gè)極簡(jiǎn)聊天室。
tips:文章結(jié)尾有開源地址,游戲數(shù)據(jù)都是本地的,改下IP運(yùn)行起來(lái)就可以玩了。
直接上干貨,首先是數(shù)據(jù)模型:
/// <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> /// 當(dāng)前出牌人 /// </summary> public int Curr { get; set; } /// <summary> /// 當(dāng)前卡片 /// </summary> public List<string> CurrCard { get; set; } = new List<string>(); /// <summary> /// 當(dāng)前卡片打出人 /// </summary> public string ExistingCardClient { get; set; } /// <summary> /// 房間成員列表 /// </summary> public List<Customer> Customers { get; set; } = new List<Customer>(); }
tips:只是單純?yōu)榱硕稤主設(shè)計(jì)的,商用版肯定不能這么搞,參考請(qǐng)慎用。
有了數(shù)據(jù)模型,自然少不了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> /// 獲取單個(gè) /// </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; //不符合出牌規(guī)則直接失敗 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> /// 更新(根據(jù)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> /// 獲取單個(gè) /// </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> /// 更新(根據(jù)房名) /// </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> /// 更新當(dāng)前出牌人 /// </summary> /// <param name="roomName"></param> /// <param name="index">傳入則強(qiáng)制修改,不傳按規(guī)則走</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]; } }
因?yàn)樗袛?shù)據(jù)都是通過(guò)靜態(tài)屬性保存的,所以大部分都是linq操作(原諒我linq水平有限)。
接下來(lái)是游戲邏輯:
/// <summary> /// 卡片相關(guān) /// </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> /// 發(fā)牌 /// </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++) { //隨機(jī)抽取一張牌 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> /// 規(guī)則 /// </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) { //現(xiàn)有牌號(hào) List<int> existingCardNo = existingCard.Select(x => Convert.ToInt32(x.Split('-')[1])).ToList().OrderBy(x => x).ToList(); //新出牌號(hào) 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; } //對(duì)子/三只 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(); //超過(guò)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; } }
單張規(guī)則和普通斗D主一樣(除了王以外2最大,其次是A),多張規(guī)則目前支持:王炸、對(duì)子、三只、順子、三帶一。目前只做到這里,各位同學(xué)可以拿回去自行擴(kuò)展。
上一些運(yùn)行圖。房主建房并加入:
新玩家加入:
房間人滿以后房主開始游戲,隨機(jī)分配地主:
出牌特效:
游戲結(jié)算:
最后附上開源地址(客戶端在web分支):https://gitee.com/muchengqingxin/card-game
tips:前端同學(xué)在沒有UI配合的情況下做到現(xiàn)在這樣,必須給個(gè)贊。最后提醒大家,不要拿去商用。
以上所述是小編給大家介紹的.Net Core使用SignalR實(shí)現(xiàn)斗地主游戲,希望對(duì)大家有所幫助。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
ASP.NET Core基礎(chǔ)之啟動(dòng)設(shè)置
這篇文章介紹了ASP.NET Core基礎(chǔ)之啟動(dòng)設(shè)置,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-02-02ASP.NET?Core在Linux下為dotnet創(chuàng)建守護(hù)進(jìn)程
本篇主要是怎么樣為我們?cè)贚inux或者macOs中部署的dotnet程序創(chuàng)建一個(gè)守護(hù)進(jìn)程,來(lái)保證我們的程序在異?;蛘呤请娔X重啟的時(shí)候仍然能夠正常訪問(wèn)。需要的朋友可以收藏下,方便下次瀏覽觀看2021-12-12初識(shí) ASP.NET Membership 用戶管理
Membership 是用做用戶管理,進(jìn)行身份認(rèn)證使用的。通過(guò)ASP.NET Membership,我們可以創(chuàng)建用戶、刪除用戶和編輯用戶屬性。所以這是一個(gè)實(shí)現(xiàn)登錄相關(guān)控件的底層框架。2016-04-04搭建基礎(chǔ)結(jié)構(gòu)的ABP解決方案介紹
這篇文章介紹了搭建基礎(chǔ)結(jié)構(gòu)的ABP解決方案的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02ASP.NET Core中的Razor頁(yè)面使用視圖組件
這篇文章介紹了ASP.NET Core中的Razor頁(yè)面使用視圖組件的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02ASP.NET Core應(yīng)用啟動(dòng)Startup類簡(jiǎn)介
這篇文章介紹了ASP.NET Core中的應(yīng)用啟動(dòng)Startup類,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04