1. 程式人生 > >Golang多執行緒簡單鬥地主

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,可以在介面上互動打牌) 邊看程式碼變講。 主函式 ```go 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”有值了,就啟用。 ``` 洗牌函式 ```go 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個牌的位置。 ``` 發牌函式 ```go 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牌桌 ```go 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函式 ```go 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執行簡單地主

多執行緒,通道,讀寫鎖(單寫多讀),隨機(洗牌),是本文涉及的主要知識點。 先看一下做出來的效果,因為是實驗程式,跟真實的鬥地主還是有差距,理解萬歲! ``` [發牌員]:洗牌咯。 刷刷刷... [發牌員]:牌洗好了。 [發牌員]:開始發牌。 [發牌員]:每個人17張牌。 [發牌員]:搶地主。 [fang

java:Map集合模擬地主,執行模擬搶地主 例項

 原始碼如下: package selfpractice.day4; import java.util.*; //多執行緒模擬搶地,重點程式碼位於loot()方法內 public class Practice_Poker { public static void main(S

java執行入門案例(2)之執行簡單應用

  上一篇文章:java多執行緒案例(1)之簡單銀行取款問題及其優化 我大概介紹了一下Java程式碼優化的問題,主要針對出學者而言,這一次我要介紹多執行緒應用的簡單案例 。網上有許多多執行緒的案例,但大多都挺複雜的,今天我主要目的也是介紹一下多執行緒應用的簡單案例,讓初學

程序和執行簡單tcp聊天程式

如果需要一個服務端可以連線多個客戶端,並同時與多個(不超多listen第二個引數及最大同時併發數)客戶端通訊,可以利用多程序即建立子程序,子程序來完成服務端的接受和傳送資料;也可以建立多個執行緒。對於tcp一些介面具體使用可以檢視這篇部落格:https://bl

Python爬蟲入門教程,執行採集圖啦表情包!

寫在前面 今天在CSDN部落格,發現好多人寫爬蟲都在爬取一個叫做鬥圖啦的網站,裡面很多表情包,然後瞅了瞅,各種實現方式都有,今天我給你實現一個多執行緒版本的。關鍵技術點 aiohttp ,你可以看一下我前面的文章,然後在學習一下。 https://github.com/wangde

Python3.6 連線mysql 資料庫,增刪改查,及執行簡單運用

readme:        匯入 pymysql 連線資料庫,完成資料處理後的增刪改查操作。匯入到其他Python檔案就可以直接呼叫。後面一個檔案是多執行緒操作, 另一個檔案是處理曲線擬合和積分的然後資料和資料庫互動的運用。 aliyunMySQL_test.py im

QT5 執行簡單實現

檔案中載入介面顯示的控制元件,變數都放在了threaddlg.h檔案中. threaddld.h原始碼如下: #ifndef THREADDLG_H #define THREADDLG_H #include <QDialog> #include <QPushButton> #i

Java執行簡單樣例(一):銀行存取錢問題

Bank類 public class Bank { private static int money; public int getMoney(){ return money; } public void saveMon

iOS 執行簡單整理NSThread、GCD、NSOperation

iOS Pthreads 和 NSThread Pthreads:可以在Unix / Linux / Windows 等系統跨平臺使用,使用 C 語言編寫,需要程式設計師自己管理執行緒的生命週期,使用難度較大 NSThread:是蘋果官方提供的,使用起來比 pthread

C# 執行 簡單的同步售票系統程式碼

using System; using System.Threading; //程序同步 //共50張票,3個視窗售賣 namespace Chapter10_Practice { cla

java執行簡單小例子2——實現Runnable介面

/** * 實現Runnable介面的類 * * @author */ public class DoSomething implements Runnable { private St

Android Studio:服務與執行--簡單音樂播放器

一、 實驗題目   服務與多執行緒--簡單音樂播放器 【目的】 1. 學會使用 MediaPlayer; 2. 學會簡單的多執行緒程式設計,使用 Handle 更新 UI; 3. 學會使用 Service 進行後臺工作; 4. 學會使用 Service 與 Activit

Asp.net Socket執行 簡單監聽埠,獲得資料

經過對上一篇文章,程式碼的二次開發得到了線面的這個方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Socket —— 通過執行簡單模擬了聊天室

/** * Created by Liwei on 2016/7/17. * 模擬一個簡單的聊天室程式 * 通過多執行緒改進剛開的聊天程式,這樣我們就可以實現在一個視窗傳送和接收資料了 */ p

java執行-簡單轉賬

/** * Created by gmy on 2017/10/14. */ public class App { public static void main(String[] args

Java執行簡單實現取消和進度

Java中簡單實現多執行緒排程時的可取消和顯示進度 一個簡單的多執行緒排程實現,統一開始,為了使得所有執行緒統一開始,類似運動員在聽到發令槍時一起進行,使用了CountDownLatch進行控制。 CountDownLatch beginLatch = new CountDownLatch(1); Cou

java.util.concurrent執行簡單demo及計算執行程式執行時間

public void doMain(String dir) { // 獲取開始時間 long startTime = System.currentTimeMillis(); try { File file = new File(dir);

goLang 執行抓取網頁資料

突然有個想法想用goLang快速的抓取網頁資料,於是想到了 多執行緒進行頁面抓取 package main import ( "fmt" "log" "net/http" "os" "st

執行,訊號量的簡單使用 GCD

基本概念 關於iOS開發中,多執行緒基本的概念和基本使用,我在這裡就不在重複說了。但是為了照顧到有的同學可能還不是對基本的概念不熟悉,可以參考一下這篇文章併發其實很簡單 說說訊號量,併發數 如果你有計算機基礎,那麼下面這段話應該很簡單就能理解 訊號量就是一個資源計數器,對訊號

【Java筆記】執行實現簡單的非同步運算

實現Callable介面,重寫call()方法,使操作執行緒池時能帶有返回值的效果: import java.util.concurrent.Callable; public class GetSumCallable implements Callable<Integer> {