Shell 脚本:地下城、龙与骰子

作者:Dave Taylor

地下城、龙与骰子——一个脚本,让你可以掷出那些令人惊讶的许多游戏所需的 3d6 和 2d20 骰子。

在我的上一篇文章中,我谈到了一个非常简单的 shell 脚本,用于一个名为 Bunco 的游戏,这是一种骰子游戏,在回合中进行,你掷三个骰子,并将你的值与回合数进行比较。如果三个都匹配并且匹配回合数,你就得到了 25 分的 bunco。否则,任何与回合匹配的骰子都值一分。这很简单——一个为在当地酒吧喝得醉醺醺的人们设计的游戏,而且也很容易编程。

Bunco 程序的核心功能是生成 1-6 之间随机数的函数,以模拟掷六面骰子。它看起来像这样


rolldie()
{
   local result=$1
   rolled=$(( ( $RANDOM % 6 ) + 1 ))
   eval $result=$rolled
}

它以变量名作为唯一参数调用,它会将 1-6 之间的随机数加载到该值中——例如


rolldie die1

将为 $die1 分配一个值 1..6。明白了吗?

但是,如果你能做到这一点,有什么能阻止你使用第二个参数来指定你想要用该函数“掷”的骰子的面数呢?像这样


rolldie()
{
   local result=$1 sides=$2
   rolled=$(( ( $RANDOM % $sides ) + 1 ))
   eval $result=$rolled
}

为了测试它,我们只需编写一个微小的包装器,简单地请求一个 20 面骰子 (d20) 的结果


rolldie die 20
echo resultant roll is $die

很简单。为了使其更有用,让我们允许用户使用标准的 D&D 符号 nDm 来指定一系列骰子滚动——也就是说,nm 面骰子。例如,Bunco 将使用 3d6 完成(三个六面骰子)。明白了吗?

由于你很可能也有起始标志,让我们使用非常方便的 getopt 将其构建到解析循环中


while getopts "h" arg
do
  case "$arg" in
    * ) echo "dnd-dice NdM {NdM}"
        echo "NdM = N M-sided dice"; exit 0 ;;
  esac
done
shift $(( $OPTIND - 1 ))
for request in $* ; do
  echo "Rolling: $request"
done

对于像 3d6 这样格式良好的符号,很容易将其参数分解为组成部分,就像这样


dice=$(echo $request | cut -dd -f1)
sides=$(echo $request | cut -dd -f2)
echo "Rolling $dice $sides-sided dice"

为了测试它,让我们给它一些参数,看看程序输出什么


$ dnd-dice 3d6 1d20 2d100 4d3 d5
Rolling 3 6-sided dice
Rolling 1 20-sided dice
Rolling 2 100-sided dice
Rolling 4 3-sided dice
Rolling  5-sided dice

啊,最后一个指出了脚本中的一个错误。如果没有指定骰子数量,默认值应为 1。理论上你也可以默认为六面骰子,但这远不如假设 1 安全。

有了这个,你就接近一个功能齐全的程序了,因为你所需要的只是一个循环来处理请求中的多个骰子。使用 while 循环很容易做到,但让我们为脚本添加一些额外的智能


for request in $* ; do
  dice=$(echo $request | cut -dd -f1)
  sides=$(echo $request | cut -dd -f2)
  echo "Rolling $dice $sides-sided dice"
  sum=0  # reset
  while [ ${dice:=1} -gt 0 ] ; do
    rolldie die $sides
    echo "     dice roll = $die"
    sum=$(( $sum + $die ))
    dice=$(( $dice - 1 ))
  done
  echo "  sum total = $sum"
done

这实际上非常可靠,虽然输出语句需要稍微清理一下,但代码基本上是完全可用的


$ dnd-dice 3d6 1d20 2d100 4d3 d5
Rolling 3 6-sided dice
     dice roll = 5
     dice roll = 6
     dice roll = 5
  sum total = 16
Rolling 1 20-sided dice
     dice roll = 16
  sum total = 16
Rolling 2 100-sided dice
     dice roll = 76
     dice roll = 84
  sum total = 160
Rolling 4 3-sided dice
     dice roll = 2
     dice roll = 2
     dice roll = 1
     dice roll = 3
  sum total = 8
Rolling  5-sided dice
     dice roll = 2
  sum total = 2

你有没有注意到我修复了 $dice 没有值的情况?它被塞进了 while 语句的引用中。我没有将其称为 $dice,而是使用了 ${dice:=1} 符号,它使用指定的值,除非它是空值或没有值,在这种情况下,值 1 被分配和使用。这很方便,并且在这种情况下是一个完美的修复。

在游戏中,你通常不太关心单个骰子的值;你只想将所有内容加起来,看看总值是多少。因此,如果你掷 4d20,例如,这只是你计算并与游戏主持人或地下城主持人分享的单个值。

稍微清理一下输出语句,你就可以做到这一点


$ dnd-dice.sh 3d6 1d20 2d100 4d3 d5
3d6 = 16
1d20 = 13
2d100 = 74
4d3 = 8
d5 = 2

让我们再次运行它,以确保你也得到不同的值


3d6 = 11
1d20 = 10
2d100 = 162
4d3 = 6
d5 = 3

肯定有不同的值,总而言之,这是一个非常有用的脚本。

你可以以此为基础创建许多变体,包括一些游戏玩家喜欢的“爆炸骰子”。这个想法很简单:如果你掷出可能的最佳值,你可以再次掷骰子并加上第二个值。掷一个 d20 并得到 20?你可以再次掷骰子,你的结果就是 20 + 第二个值。疯狂之处在于你可以多次循环执行此操作,因此 d20 可以变成 30、40 甚至 50。

这篇文章就到此为止。在这一点上,关于骰子没有什么可做的了。在我的下一篇文章中,我将看看……好吧,你必须拭目以待!别忘了,如果你有想让我处理的主题,请给我发个消息!

Dave Taylor 长期以来一直在 UNIX 和 Linux 系统上编写 shell 脚本。他是 Learning Unix for Mac OS XWicked Cool Shell Scripts 的作者。你可以在 Twitter 上找到他 @DaveTaylor,你也可以通过他的技术问答网站联系他:Ask Dave Taylor

加载 Disqus 评论