多线程,通道,读写锁(单写多读),随机(洗牌),是本文涉及的主要知识点。

先看一下做出来的效果,因为是实验程序,跟真实的斗地主还是有差距,理解万岁!

[发牌员]:洗牌咯。
刷刷刷...
[发牌员]:牌洗好了。
[发牌员]:开始发牌。
[发牌员]:每个人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多线程简单斗地主的更多相关文章

  1. iOS开发多线程篇—多线程简单介绍

    iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...

  2. python多线程简单例子

    python多线程简单例子 作者:vpoet mail:vpoet_sir@163.com import thread def childthread(threadid): print "I ...

  3. Golang设计模式—简单工厂模式(Simple Factory Pattern)

    Golang设计模式--简单工厂模式 背景 假设我们在做一款小型翻译软件,软件可以将德语.英语.日语都翻译成目标中文,并显示在前端. 思路 我们会有三个具体的语言翻译结构体,或许以后还有更多,但现在分 ...

  4. Java的多线程 简单入门

    Java的多线程 简单入门 首先能够先搞清楚什么是程序.进程.线程,以及它们之间的关系: 定义: 一 程序仅仅是一组指令的有序集合.它是静态的 二 进程是具有一定独立功能的程序关于某个数据集合上的一次 ...

  5. C#多线程简单例子讲解

    C#多线程简单例子讲解 标签: 多线程c#threadobjectcallbacktimer 分类: C#(7) 转载网址:http://www.knowsky.com/540518.html .NE ...

  6. 代码片段 - Golang 实现简单的 Web 服务器

    ------------------------------ 下面一段代码,实现了最简单的 Web 服务器: 编译环境: Linux Mint 18 Cinnamon 64-bit Golang 1. ...

  7. Asp.net core与golang web简单对比测试

    最近因为工作需要接触了go语言,又恰好asp.net core发布RC2,就想简单做个对比测试. 下面是测试环境: CPU:E3-1230 v2 内存:16G 电脑有点不给力 操作系统:Centos7 ...

  8. iOS 多线程 简单学习NSThread NSOperation GCD

    1:首先简单介绍什么叫线程 可并发执行的,拥有最小系统资源,共享进程资源的基本调度单位. 共用堆,自有栈(官方资料说明iOS主线程栈大小为1M,其它线程为512K). 并发执行进度不可控,对非原子操作 ...

  9. golang从简单的即时聊天来看架构演变

    前言 俗话说的好,架构从来都不是一蹴而就的,没有什么架构一开始设计就是最终版本,其中需要经过很多步骤的变化,今天我们就从一个最简单的例子来看看,究竟架构这个东西是怎么变的. 我将从一个最简单的聊天室的 ...

随机推荐

  1. C#LeetCode刷题之#766-托普利茨矩阵(Toeplitz Matrix)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3748 访问. 如果一个矩阵的每一方向由左上到右下的对角线上具有相 ...

  2. Vercel托管博客

    背景 本站已托管至Vercel,但作为一名前端小白,昨天帮朋友托管博客到Vercel的过程中到处碰壁,就想写一篇博客记录一下. 注册登录 点击Vercel官网{% btn 'https://verce ...

  3. 【POJ2976】Dropping tests - 01分数规划

    Description In a certain course, you take n tests. If you get ai out of bi questions correct on test ...

  4. 【全解】Eclipse添加Spring项目插件

    1.Eclipse打开window-preference-InstallNewSoftware 2.先点Manage,取消掉The Eclipse Project Updates 3.选择Add . ...

  5. 精讲响应式WebClient第2篇-GET请求阻塞与非阻塞调用方法详解

    本文是精讲响应式WebClient第2篇,前篇的blog访问地址如下: 精讲响应式webclient第1篇-响应式非阻塞IO与基础用法 在上一篇文章为大家介绍了响应式IO模型和WebClient的基本 ...

  6. rabbitmq python demo 参考链接地址

    链接地址: https://docs.openstack.org/oslo.messaging/latest/reference/server.html https://www.cnblogs.com ...

  7. Leecode统计子串个数(java)

    /** 获取一个字符串在另一个字符串中出现的次数.判断str2在str1中出现的次数 */ public class StringExer2 { public static void main(Str ...

  8. 计算机网络-应用层(4)DNS协议

    域名系统(Domain Name System, DNS):一个分层的由DNS服务器实现的分布式数据库+一个使得主机能够查询分布式数据库的应用层协议 DNS服务器通常是运行BIND (Berkeley ...

  9. 第五篇 Scrum冲刺博客

    一.会议图片 二.项目进展 成员 完成情况 今日任务 冯荣新 未完成 购物车列表,购物车工具栏 陈泽佳 未完成 静态结构 徐伟浩 商品信息录入 协助前端获取数据 谢佳余 未完成 搜索算法设计 邓帆涛 ...

  10. 小白一样能建站——winser2012 IIS8.0搭建基本的网站

    在window server 2012环境下,搭建一个基本的 网站.能够使用即可. 打开服务器管理器 添加角色和功能 默认下一步 下一步, 下一步,选择web服务器 添加功能 下一步, 下一步,不安装 ...