960x60
  当前位置:海洋目录网 » 站长资讯 » 站长资讯 » 文章详细 订阅RssFeed

Go编程(二) 多线程简单斗地主

来源:本站原创 浏览:209次 时间:2021-05-01

多线程简单斗地主。

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

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

[发牌员]:洗牌咯。刷刷刷...[发牌员]:牌洗好了。[发牌员]:开始发牌。[发牌员]:每个人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])// Winnerwinner := <-winnerfmt.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 [][]stringdealCards = 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.RWMutexplayCards []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 :=  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 := 0for 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 += 1if i == len(hands) {winner <- namebreak}}}}

解析:

1.玩家函数,第一个参数是名字,第二个参数是手上拿的牌。2.3个线程都有这样一段代码。3.首先从通道“bottom”读取值,也就是抢地主。4.抢到地主的玩家,会把底牌放到自己的手牌中,并且把自己的名字写到牌桌上(根据名字来看该谁出牌),地主先出牌。5.for true {}循环不停的出牌,从第一张到最后一张,先从牌桌上看是不是自己的名字,是自己的名字才轮到出牌。6.牌出完了,就把自己的名字写到通道“winner”,游戏结束。

本文的程序只是为了实验go的多线程特性,不具备可玩性,期待更多的同学请见谅。
如果需要源码的话,请到公众号回复「斗地主」获取哦。

  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net