歡迎光臨
每天分享高質量文章

指令碼程式設計之骰子游戲 | Linux 中國

Bunco:一個使你的“快艇”遊戲看起來更複雜的擲骰子游戲。
— Dave Taylor


本文導航
編譯自 | http://www.linuxjournal.com/content/shell-scripting-bunco-game 
 作者 | Dave Taylor
 譯者 | wenwensnow

Bunco:一個使你的“快艇”遊戲看起來更複雜的擲骰子游戲。

我已經有段時間沒有編寫遊戲了,所以我覺得現在正是做一些這方面事情的時候。起初,我想“用指令碼編一個 Halo遊戲?”(LCTT 譯註:Halo,光暈系列遊戲),但我後來意識到這不太可能。來編一個叫 Bunco 的簡單骰子游戲吧。你也許沒有聽說過,不過你母親絕對知道 —— 當一群年輕女孩聚在當地的酒吧或者小酒館的時候,這是個很受歡迎的遊戲。

遊戲一共六輪,有三個骰子,規則很簡單。每次投三個骰子,投出的點數要和當前的輪數數字一致。如果三個骰子都和當前的輪數一致,(比如,在第三輪三個骰子都是 3),你這一輪的分數就是 21。 如果三個骰子點數都相同但和輪數數字不同,你會得到最低的 Bunco 分數,只有 5 分。如果你投出的點數兩者都不是,每一個和當前輪數相同的骰子得 1 分。

要想玩這個遊戲,它還涉及到團隊合作,每一隊(包括贏的那隊),每人付 5 美元現金,或贏家得到其他類似現金獎勵,並規定什麼樣的情況下才是贏家,例如“最多 Buncos” 或“最大點數”的情況下勝利。在這裡我會跳過這些,而只關註投骰子這一部分。

關於數學邏輯部分

在專註於程式設計這方面的事之前,我先簡單說說遊戲背後的數學邏輯。要是有一個適當重量的骰子投骰子會變得很容易,任意一個值出現機率都是 1/6。

完全隨機小提示:不確定你的骰子是否每個面都是一樣重量? 把它們扔進鹽水裡然後擲一下。YouTube 上有一些有趣的 D&D; 世界的影片向你展示了怎麼來做這個測試。

所以三個骰子點數一樣的機率有多大? 第一個骰子 100% 會有一個值 (這兒沒什麼可說的),所以很簡單。第二個則有 16.66% 的機率和第一個骰子的值一樣,接下來第三個骰子也是一樣。 但當然,總機率是三個機率相乘的結果,所以最後,三個骰子值相等的機率是 2.7%。

接下來,每個骰子和當前輪數數字相同的機率都是 16.66%。從數學角度來說:0.166 * 0.166 * 0.166 = 0.00462 。

換句話說,你有 0.46% 的可能性投出 Bunco,比 200 次中出現一次的可能性還小一點。

實際上還可以更難。如果你有 5 個骰子,投出 Mini Bunco (也可以叫做 Yahtzee “快艇”) 的機率為 0.077%,如果你想所有的骰子的值都相同,假設都是 6,那機率就是 0.00012%,那就基本上沒什麼可能了。

開始程式設計吧

和所有遊戲一樣,最難的部分是有一個能生成真正的隨機數的隨機數發生器。這一部分在 shell 指令碼中還是很難實現的,所以我需要先迴避這個問題,並假設 shell 內建的隨機數發生器就夠用了。

不過好在內建的隨機數發生器很好用。用 $RANDOM 就能得到一個 0 到 MAXINT(32767) 之間的隨機值:

  1. $ echo $RANDOM $RANDOM $RANDOM

  2. 10252 22142 14863

為了確保產生的值一定是 1 – 6 之中的某個值,使用取餘函式:

  1. $ echo $(( $RANDOM % 6 ))

  2. 3

  3. $ echo $(( $RANDOM % 6 ))

  4. 0

哦!我忘了要加 1,下麵是另一次嘗試:

  1. $ echo $(( ( $RANDOM % 6 ) + 1 ))

  2. 6

下麵要實現投骰子這一功能。這個函式中你可以宣告一個區域性變數來儲存生成的隨機值:

  1. rolldie()

  2. {

  3.   local result=$1

  4.   rolled=$(( ( $RANDOM % 6 ) + 1 ))

  5.   eval $result=$rolled

  6. }

使用 eval 確保生成的隨機數被實際儲存在變數中。這一部分也很容易:

  1. rolldie die1

這會為第一個骰子生成一個 1 – 6 之間的隨機值儲存到 die1 中。要擲 3 個骰子,很簡單:

  1. rolldie die1 ; rolldie die2 ; rolldie die3

現在判斷下生成的值。首先,判斷是不是 Bunco(3 個骰子值相同),然後是不是和當前輪數值也相同:

  1. if [ $die1 -eq $die2 ] && [ $die2 -eq $die3 ] ; then

  2.  if [ $die1 -eq $round ] ; then

  3.    echo "BUNCO!"

  4.    score=25

  5.  else

  6.    echo "Mini Bunco!"

  7.    score=5

  8.  fi

這可能是所有判斷陳述句中最難的部分了,註意第一個條件陳述句中這種不常用的寫法 : [ cond1 ] && [ cond2 ]。如果你想寫成 cond1 -a cond2 ,這樣也可以。在 shell 程式設計中,解決問題的方法往往不止一種。

程式碼剩下的部分很直白,你只需要判斷每個骰子的值是不是和本輪數字相同:

  1. if [ $die1 -eq $round ] ; then

  2.  score=1

  3. fi

  4. if [ $die2 -eq $round ] ; then

  5.  score=$(( $score + 1 ))

  6. fi

  7. if [ $die3 -eq $round ] ; then

  8.  score=$(( $score + 1 ))

  9. fi

唯一要註意的是當出現 Bunco/Mini Bunco 就不需要再統計本輪分數了。所以整個第二部分的判斷陳述句都要寫在第一個條件陳述句的 else 中(為了判斷 3 個骰子值是否都相同)。

把所有的綜合起來,然後在命令列中輸入輪數,下麵是現在的指令碼執行後的結果:

  1. $ sh bunco.sh 5

  2. You rolled: 1 1 5

  3. score = 1

  4. $ sh bunco.sh 2

  5. You rolled: 6 4 3

  6. score = 0

  7. $ sh bunco.sh 1

  8. You rolled: 1 1 1

  9. BUNCO!

  10. score = 25

竟然這麼快就出現 Bunco 了? 好吧,就像我說的,shell 內建的隨機數發生器在隨機數產生這方面可能有些問題。

你可以再寫個指令碼測試一下,去執行上述指令碼幾百次,然後看看 Bunco/Mini Bunco 出現次數所佔的百分比。但是我想把這部分作為練習,留給親愛的讀者你們。不過,也許我下次會抽時間完成剩下的部分。

讓我們完成這一指令碼吧,還有分數統計和一次性執行 6 次投骰子(這次不用再在命令列中手動輸入當前輪數了)這兩個功能。這也很容易,因為只是將上面的內容整個巢狀在裡面,換句話說,就是將一個複雜的條件巢狀結構全部寫在了一個函式中:

  1. BuncoRound()

  2. {

  3.   # roll, display, and score a round of bunco!

  4.   # round is specified when invoked, score added to totalscore

  5.   local score=0 ; local round=$1 ; local hidescore=0

  6.   rolldie die1 ; rolldie die2 ; rolldie die3

  7.   echo Round $round. You rolled: $die1 $die2 $die3

  8.   if [ $die1 -eq $die2 ] && [ $die2 -eq $die3 ] ; then

  9.     if [ $die1 -eq $round ] ; then

  10.       echo "  BUNCO!"

  11.       score=25

  12.       hidescore=1

  13.     else

  14.       echo "  Mini Bunco!"

  15.       score=5

  16.       hidescore=1

  17.     fi

  18.   else

  19.     if [ $die1 -eq $round ] ; then

  20.       score=1

  21.     fi

  22.     if [ $die2 -eq $round ] ; then

  23.       score=$(( $score + 1 ))

  24.     fi

  25.     if [ $die3 -eq $round ] ; then

  26.       score=$(( $score + 1 ))

  27.     fi

  28.   fi

  29.   if [ $hidescore -eq 0 ] ; then

  30.     echo "  score this round: $score"

  31.   fi

  32.   totalscore=$(( $totalscore + $score ))

  33. }

我承認,我忍不住自己做了一點改進,包括判斷當前是 Bunco、Mini Bunco 還是其他需要計算分數的情況這一部分 (這就是 $hidescore 這一變數的作用)。

實現這個簡直是小菜一碟,只要一個迴圈就好了:

  1. for round in {1..6} ; do

  2.  BuncoRound $round

  3. done

這就是現在所寫的整個程式。讓我們執行一下看看結果:

  1. $ sh bunco.sh 1

  2. Round 1\. You rolled: 2 3 3

  3.  score this round: 0

  4. Round 2\. You rolled: 2 6 6

  5.  score this round: 1

  6. Round 3\. You rolled: 1 2 4

  7.  score this round: 0

  8. Round 4\. You rolled: 2 1 4

  9.  score this round: 1

  10. Round 5\. You rolled: 5 5 6

  11.  score this round: 2

  12. Round 6\. You rolled: 2 1 3

  13.  score this round: 0

  14. Game over. Your total score was 4

嗯。並不是很令人滿意,可能是因為它只是遊戲的一次完整執行。不過,你可以將指令碼執行幾百幾千次,記下“Game over”出現的位置,然後用一些快速分析工具來看看你在每 6 輪中有幾次得分超過 3 分。(要讓 3 個骰子值相同,這個機率大概在 50% 左右)。

無論怎麼說,這都不是一個複雜的遊戲,但是它是一個很有意思的小程式專案。現在,如果有一個 20 面的骰子,每一輪遊戲有好幾十輪,每輪都擲同一個骰子,情況又會發生什麼變化呢?


via: http://www.linuxjournal.com/content/shell-scripting-bunco-game

作者:Dave Taylor[2] 譯者:wenwensnow 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

LCTT 譯者

wenwensnow ? ?
共計翻譯:6 篇
貢獻時間:70 天


推薦文章

< 左右滑動檢視相關文章 >

點選圖片、輸入文章 ID 或識別二維碼直達

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖