Golang多线程简单斗地主
多线程,通道,读写锁(单写多读),随机(洗牌),是本文涉及的主要知识点。
先看一下做出来的效果,因为是实验程序,跟真实的斗地主还是有差距,理解万岁!
[发牌员]:洗牌咯。
刷刷刷...
[发牌员]:牌洗好了。
[发牌员]:开始发牌。
[发牌员]:每个人17张牌。
[发牌员]:抢地主。
[fang]:哈哈,我是地主!
fang的牌是[9 9 A 9 6 5 3 10 5 8 Q A 8 4 4 K 7 A K 3],共20张。
dong的牌是[大王 8 5 小王 6 Q 10 7 3 A Q J K 6 9 Q 2],共17张。
er的牌是[A K 3 2 4 2 5 K 10 2 8 6 4 J 3 J 7],共17张。
[fang]:我开始出牌了。
[er]:我开始出牌了。
[dong]:我开始出牌了。
赢家是er。
基本流程是洗牌->发牌->抢地主->打牌->gg。
哈哈这个程序的精髓是,由于时(lan)间(de)有(xie)限(le),打牌是哪个线程抢到了就出牌,直到牌出完了,就赢了。(多线程写斗地主,是我大学操作系统课程的实验项目,当时是完整实现了斗地主算法的,用的是C++和MFC,可以在界面上交互打牌)
边看代码变讲。
主函数
func main() {
// 洗牌
cards := shuffle()
// 发牌
dealCards := deal(cards)
// 抢地主
fmt.Println("[发牌员]:抢地主。")
go player(order[0], dealCards[0])
go player(order[1], dealCards[1])
go player(order[2], dealCards[2])
// Winner
winner := <-winner
fmt.Printf("赢家是%s。\n", winner)
}
解析:
1.main里面是打牌的步骤,洗牌,发牌,抢地主,打牌,gg。
2.用go player(),开了3个线程,也就是3个玩家。
3.发牌的时候,是留了3张底牌的,存在通道“bottom”里面,抢地主的时候,3个线程就去取,谁先取到谁就是地主。
4.打牌打到最后,会往另外一个通道“winner”里面写值,谁先打完,就把自己的name存进去。
5.3个玩家在打牌的时候,main是阻塞的,等待从通道“winner”读取值,有玩家打完了,通道“winner”有值了,就激活。
洗牌函数
func shuffle() []string {
fmt.Println("[发牌员]:洗牌咯。")
fmt.Println("刷刷刷...")
cards := cards()
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(cards), func(i, j int) {
cards[i], cards[j] = cards[j], cards[i]
})
fmt.Println("[发牌员]:牌洗好了。")
return cards
}
解析:
1.rand默认是假的随机,因为不管运行多少次都是一样的,需要设置种子,time.Now().UnixNano(),让每次随机结果都不同。
2.rand.Shuffle()洗牌,随机交换2个牌的位置。
发牌函数
func deal(cards []string) [][]string {
fmt.Println("[发牌员]:开始发牌。")
var dealCards [][]string
dealCards = append(dealCards, cards[0:17])
dealCards = append(dealCards, cards[17:34])
dealCards = append(dealCards, cards[34:51])
fmt.Println("[发牌员]:每个人17张牌。")
go leaveBottom(cards[51:54])
return dealCards
}
解析:
1.因为已经洗了牌了,直接先切3份牌出来,每份17张。
2.留了3张底牌,放到通道“bottom”中。
3.如果这里不再开线程,会发生死锁!因为main本身也是个线程,直接存通道的话,会把main阻塞,直到有线程把通道的值读出去;而main阻塞后,是无法继续执行后面的代码的,也就无法再起3个玩家线程来读值了,就会发生死锁。
4.所以leaveBottom()起了一个单独的线程。
Desk牌桌
type Desk struct {
mutex sync.RWMutex
playCards []string
}
func (d *Desk) write(card string) {
d.mutex.Lock()
defer d.mutex.Unlock()
d.playCards = append(d.playCards, card)
}
func (d *Desk) read() []string {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.playCards
}
解析:
1.定义了结构Desk,包括读写锁和牌桌上打的牌。
2.定义了write()和read()2个函数,3个线程可以同时读,但只能一次写,也就是单写多读锁。
player函数
func player(name string, hands []string) {
landlord := <-bottom
if len(landlord) > 0 {
fmt.Printf("[%s]:哈哈,我是地主!\n", name)
hands = append(hands, landlord...)
desk.write(name)
}
fmt.Printf("%s的牌是%s,共%d张。\n", name, hands, len(hands))
time.Sleep(time.Second)
i := 0
for true {
playCards := desk.read()
if playCards[len(playCards)-1] == name {
if i == 1 {
fmt.Printf("[%s]:我开始出牌了。\n", name)
}
desk.write(hands[i])
desk.write(order[(getOrderID(name)+1)%3])
i += 1
if i == len(hands) {
winner <- name
break
}
}
}
}
解析:
1.玩家函数,第一个参数是名字,第二个参数是手上拿的牌。
2.3个线程都有这样一段代码。
3.首先从通道“bottom”读取值,也就是抢地主。
4.抢到地主的玩家,会把底牌放到自己的手牌中,并且把自己的名字写到牌桌上(根据名字来看该谁出牌),地主先出牌。
5.for true {}循环不停的出牌,从第一张到最后一张,先从牌桌上看是不是自己的名字,是自己的名字才轮到出牌。
6.牌出完了,就把自己的名字写到通道“winner”,游戏结束。
本文的程序只是为了实验go的多线程特性,不具备可玩性,期待更多的同学请见谅。
如果需要源码的话,请到公众号回复「斗地主」获取哦。
版权申明:本文为博主原创文章,转载请保留原文链接及作者。
如果您喜欢我写的文章,请关注公众号支持一下,谢谢哈哈哈。

Golang多线程简单斗地主的更多相关文章
- iOS开发多线程篇—多线程简单介绍
iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...
- python多线程简单例子
python多线程简单例子 作者:vpoet mail:vpoet_sir@163.com import thread def childthread(threadid): print "I ...
- Golang设计模式—简单工厂模式(Simple Factory Pattern)
Golang设计模式--简单工厂模式 背景 假设我们在做一款小型翻译软件,软件可以将德语.英语.日语都翻译成目标中文,并显示在前端. 思路 我们会有三个具体的语言翻译结构体,或许以后还有更多,但现在分 ...
- Java的多线程 简单入门
Java的多线程 简单入门 首先能够先搞清楚什么是程序.进程.线程,以及它们之间的关系: 定义: 一 程序仅仅是一组指令的有序集合.它是静态的 二 进程是具有一定独立功能的程序关于某个数据集合上的一次 ...
- C#多线程简单例子讲解
C#多线程简单例子讲解 标签: 多线程c#threadobjectcallbacktimer 分类: C#(7) 转载网址:http://www.knowsky.com/540518.html .NE ...
- 代码片段 - Golang 实现简单的 Web 服务器
------------------------------ 下面一段代码,实现了最简单的 Web 服务器: 编译环境: Linux Mint 18 Cinnamon 64-bit Golang 1. ...
- Asp.net core与golang web简单对比测试
最近因为工作需要接触了go语言,又恰好asp.net core发布RC2,就想简单做个对比测试. 下面是测试环境: CPU:E3-1230 v2 内存:16G 电脑有点不给力 操作系统:Centos7 ...
- iOS 多线程 简单学习NSThread NSOperation GCD
1:首先简单介绍什么叫线程 可并发执行的,拥有最小系统资源,共享进程资源的基本调度单位. 共用堆,自有栈(官方资料说明iOS主线程栈大小为1M,其它线程为512K). 并发执行进度不可控,对非原子操作 ...
- golang从简单的即时聊天来看架构演变
前言 俗话说的好,架构从来都不是一蹴而就的,没有什么架构一开始设计就是最终版本,其中需要经过很多步骤的变化,今天我们就从一个最简单的例子来看看,究竟架构这个东西是怎么变的. 我将从一个最简单的聊天室的 ...
随机推荐
- 2020-04-05:谈一下spring事务传播
- C#算法设计排序篇之08-计数排序(附带动画演示程序)
计数排序(Counting Sort) 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/689 访问. 计数排序是一个非基 ...
- 编译原理 First集和Follow集的求法
转载地址 https://blog.csdn.net/Alexander_Frank/article/details/51280798 自上而下分析: FIRST集求法 First集合最终是对产生式右 ...
- 面试不知如何回答这六大知识点,你还敢说熟悉MySQL?
文章目录 一.事务 1. 什么是事务 2. 事务的四大特性 3. 事务的并发问题 ① 事务并发问题什么时候发生? ② 事务的并发问题有哪些? ③ 如何避免事务的并发问题? 二.索引 1. 什么是索引 ...
- Typora安装教程
一:Typora介绍 Markdown是一种轻量级标记语言.通过简单的标记语法,它可以使普通文本内容具有一定的格式.所谓轻量级标记语言指的是一类用简单句法描述简单格式的文本语言. 二:下载地址 ...
- unity探索者之复制内容到剪贴板
版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/8417014.html unity中提供的TextEditor类,只能在window ...
- JavaScript学习系列博客_11_JavaScript中的for语句
for循环 - 语法: for(①初始化表达式 ; ②条件表达式 ; ④更新表达式){ ③语句... } - 执行流程: 首先执行①初始化表达式,初始化一个变量,(这里只会执行一次) 然后对②条件表达 ...
- 什么是谷歌PageRank (简称PR值)
http://www.wocaoseo.com/thread-213-1-1.html 谷歌pageRank是谷歌用来评测网页质量高低的一个工具,主要分为0到10共11个等级,目前有很多的工具或谷歌工 ...
- [C#.NET 拾遗补漏]08:强大的LINQ
大家好,这是 [C#.NET 拾遗补漏] 系列的第 08 篇文章,今天讲 C# 强大的 LINQ 查询.LINQ 是我最喜欢的 C# 语言特性之一. LINQ 是 Language INtegrate ...
- DataPicker 日期选择器
问题描述:DataPicker 日期选择器,如何选取时间段(本日,昨日,上周,上月,当月) 代码实现: <el-date-picker v-model="searchForm.data ...