脚本编程之骰子游戏 | Linux 中国

脚本编程之骰子游戏 | Linux 中国
Bunco:一个使你的“快艇”游戏看起来更复杂的掷骰子游戏。-- Dave Taylor

本文导航? 关于数学逻辑部分

15%

? 开始编程吧

29%

编译自 | 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) 之间的随机值:

  • $

    echo

    $RANDOM $RANDOM $RANDOM

  • 10252

    22142

    14863

  • 为了确保产生的值一定是 1 - 6 之中的某个值,使用取余函数:

  • $

    echo

    $

    ((

    $RANDOM

    %

    6

    ))

  • 3

  • $

    echo

    $

    ((

    $RANDOM

    %

    6

    ))

  • 0

  • 哦!我忘了要加 1,下面是另一次尝试:

  • $

    echo

    $

    ((

    (

    $RANDOM

    %

    6

    )

    +

    1

    ))

  • 6

  • 下面要实现投骰子这一功能。这个函数中你可以声明一个局部变量来存储生成的随机值:

  • rolldie

    ()

  • {

  •  

    local

    result

    =

    $1

  •   rolled

    =

    $

    ((

    (

    $RANDOM

    %

    6

    )

    +

    1

    ))

  •  

    eval

    $result

    =

    $rolled

  • }

  • 使用 eval 确保生成的随机数被实际存储在变量中。这一部分也很容易:

  • rolldie die1

  • 这会为第一个骰子生成一个 1 - 6 之间的随机值存储到 die1 中。要掷 3 个骰子,很简单:

  • rolldie die1

    ;

    rolldie die2

    ;

    rolldie die3

  • 现在判断下生成的值。首先,判断是不是 Bunco(3 个骰子值相同),然后是不是和当前轮数值也相同:

  • if

    [

    $die1

    -

    eq $die2

    ]

    &&

    [

    $die2

    -

    eq $die3

    ]

    ;

    then

  •  

    if

    [

    $die1

    -

    eq $round

    ]

    ;

    then

  •    

    echo

    "BUNCO!"

  •    score

    =

    25

  •  

    else

  •    

    echo

    "Mini Bunco!"

  •    score

    =

    5

  •  

    fi

  • 这可能是所有判断语句中最难的部分了,注意第一个条件语句中这种不常用的写法 : [ cond1 ] && [ cond2 ]。如果你想写成 cond1 -a cond2 ,这样也可以。在 shell 编程中,解决问题的方法往往不止一种。

    代码剩下的部分很直白,你只需要判断每个骰子的值是不是和本轮数字相同:

  • if

    [

    $die1

    -

    eq $round

    ]

    ;

    then

  •  score

    =

    1

  • fi

  • if

    [

    $die2

    -

    eq $round

    ]

    ;

    then

  •  score

    =

    $

    ((

    $score

    +

    1

    ))

  • fi

  • if

    [

    $die3

    -

    eq $round

    ]

    ;

    then

  •  score

    =

    $

    ((

    $score

    +

    1

    ))

  • fi

  • 唯一要注意的是当出现 Bunco/Mini Bunco 就不需要再统计本轮分数了。所以整个第二部分的判断语句都要写在第一个条件语句的 else 中(为了判断 3 个骰子值是否都相同)。

    把所有的综合起来,然后在命令行中输入轮数,下面是现在的脚本执行后的结果:

  • $ sh bunco

    .

    sh

    5

  • You

    rolled

    :

    1

    1

    5

  • score

    =

    1

  • $ sh bunco

    .

    sh

    2

  • You

    rolled

    :

    6

    4

    3

  • score

    =

    0

  • $ sh bunco

    .

    sh

    1

  • You

    rolled

    :

    1

    1

    1

  • BUNCO

    !

  • score

    =

    25

  • 竟然这么快就出现 Bunco 了? 好吧,就像我说的,shell 内置的随机数发生器在随机数产生这方面可能有些问题。

    你可以再写个脚本测试一下,去运行上述脚本几百次,然后看看 Bunco/Mini Bunco 出现次数所占的百分比。但是我想把这部分作为练习,留给亲爱的读者你们。不过,也许我下次会抽时间完成剩下的部分。

    让我们完成这一脚本吧,还有分数统计和一次性执行 6 次投骰子(这次不用再在命令行中手动输入当前轮数了)这两个功能。这也很容易,因为只是将上面的内容整个嵌套在里面,换句话说,就是将一个复杂的条件嵌套结构全部写在了一个函数中:

  • BuncoRound

    ()

  • {

  •  

    #

    roll

    ,

    display

    ,

    and

    score a round of bunco

    !

  •  

    #

    round

    is

    specified

    when

    invoked

    ,

    score added to totalscore

  •  

    local

    score

    =

    0

    ;

    local

    round

    =

    $1

    ;

    local

    hidescore

    =

    0

  •   rolldie die1

    ;

    rolldie die2

    ;

    rolldie die3

  •  

    echo

    Round

    $round

    .

    You

    rolled

    :

    $die1 $die2 $die3

  •  

    if

    [

    $die1

    -

    eq $die2

    ]

    &&

    [

    $die2

    -

    eq $die3

    ]

    ;

    then

  •    

    if

    [

    $die1

    -

    eq $round

    ]

    ;

    then

  •      

    echo

    "  BUNCO!"

  •       score

    =

    25

  •       hidescore

    =

    1

  •    

    else

  •      

    echo

    "  Mini Bunco!"

  •       score

    =

    5

  •       hidescore

    =

    1

  •    

    fi

  •  

    else

  •    

    if

    [

    $die1

    -

    eq $round

    ]

    ;

    then

  •       score

    =

    1

  •    

    fi

  •    

    if

    [

    $die2

    -

    eq $round

    ]

    ;

    then

  •       score

    =

    $

    ((

    $score

    +

    1

    ))

  •    

    fi

  •    

    if

    [

    $die3

    -

    eq $round

    ]

    ;

    then

  •       score

    =

    $

    ((

    $score

    +

    1

    ))

  •    

    fi

  •  

    fi

  •  

    if

    [

    $hidescore

    -

    eq

    0

    ]

    ;

    then

  •    

    echo

    "  score this round: $score"

  •  

    fi

  •   totalscore

    =

    $

    ((

    $totalscore

    +

    $score

    ))

  • }

  • 我承认,我忍不住自己做了一点改进,包括判断当前是 Bunco、Mini Bunco 还是其他需要计算分数的情况这一部分 (这就是 $hidescore 这一变量的作用)。

    实现这个简直是小菜一碟,只要一个循环就好了:

  • for

    round

    in

    {

    1.

    .

    6

    }

    ;

    do

  •  

    BuncoRound

    $round

  • done

  • 这就是现在所写的整个程序。让我们执行一下看看结果:

  • $ sh bunco

    .

    sh

    1

  • Round

    1

    \.

    You

    rolled

    :

    2

    3

    3

  •  score

    this

    round

    :

    0

  • Round

    2

    \.

    You

    rolled

    :

    2

    6

    6

  •  score

    this

    round

    :

    1

  • Round

    3

    \.

    You

    rolled

    :

    1

    2

    4

  •  score

    this

    round

    :

    0

  • Round

    4

    \.

    You

    rolled

    :

    2

    1

    4

  •  score

    this

    round

    :

    1

  • Round

    5

    \.

    You

    rolled

    :

    5

    5

    6

  •  score

    this

    round

    :

    2

  • Round

    6

    \.

    You

    rolled

    :

    2

    1

    3

  •  score

    this

    round

    :

    0

  • 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 译者脚本编程之骰子游戏 | Linux 中国
    wenwensnow ?? ??共计翻译:

    6

     篇贡献时间:70 天

    推荐文章

    < 左右滑动查看相关文章 >

    脚本编程之骰子游戏 | Linux 中国
    脚本编程之骰子游戏 | Linux 中国
    脚本编程之骰子游戏 | Linux 中国
    脚本编程之骰子游戏 | Linux 中国
    脚本编程之骰子游戏 | Linux 中国
    脚本编程之骰子游戏 | Linux 中国
    脚本编程之骰子游戏 | Linux 中国

    点击图片、输入文章 ID 或识别二维码直达

    原文链接请访问“

    原文链接

    ”获得可点击的文内链接、全尺寸原图和相关文章。