Work the Shell - 编写 Shell 游戏

作者:Dave Taylor

在过去的三篇文章中,我们一直在讨论 shell 脚本编程的基本要素,所以我认为现在是时候开始深入研究一个真正的 shell 脚本,并构建一些有趣且有用的东西了。好吧,至少是有趣!

我想做的是——并且我预先承认这对于 shell 脚本来说可能是一个非常困难的问题——尝试编写一个基本的 二十一点 游戏。它足够简单,应该可以管理,但也足够难,我们真的需要发挥我们的脚本编写能力才能使一切正常运行。不用说,它不会有花哨的图形界面!

前往拉斯维加斯

我们将根据需要深入探讨 二十一点 的具体规则,但现在您只需要知道关于 二十一点 的知识是,每位玩家都会从一副标准扑克牌中获得两张牌,然后玩家可以请求额外的牌,试图使他们的点数总和尽可能接近 21 点,而又不超过该值。所有花牌都值 10 点,而 A 可以算作 1 点或 11 点,具体取决于玩家希望如何计算。

第一个挑战是创建一个虚拟牌堆,但这比您想象的要容易,因为它可以用一个简单的 52 个元素的数组来表示,前 13 个元素代表一种花色,第二个 13 个元素代表第二种花色,依此类推。例如,第 37 张牌可能是红桃 J。

事实证明,shell 脚本可以使用数组,所以让我们从创建一个 52 元素数组并用值 1-52 填充它开始

card=1
while [ $card -lt 53 ]
do
  deck[$card]=$card
  card=$(( $card + 1 ))
done

如果您习惯使用 Perl,您可能会认为 for 循环对于此类任务来说是更合乎逻辑的选择,但是 shell 脚本中的 for 循环缺乏遍历值范围的能力。Bourne Shell 中的数组很容易使用:只需指定一个引用索引,数组就会动态增长到该大小。

现在我们有了牌堆的表示,但它是完全排序的,所以下一步是编写一些代码来洗牌。正如您可能预料的那样,这证明有点棘手!

基本思路是我们将随机选择一个介于 1 和 52 之间的数字,然后查看其对应的牌是否可用。因此,我们创建的已排序的初始牌堆用作洗牌后的牌堆的来源,洗牌后的牌堆实际上将最终放入一个新数组中。这是用于随机选牌的基本代码段

while [ $errcount -lt $threshold ]
 do
   randomcard=$(( ( $RANDOM % 52 ) + 1 ))
   errcount=$(( $errcount + 1 ))

   if [ ${deck[$randomcard]} -ne 0 ] ; then
     picked=${deck[$randomcard]}
     deck[$picked]=0         # picked, remove it
     return $picked
   fi
 done

这里有很多内容,但让我们先谈谈基本逻辑:虽然我们将随机选择一张介于 1 和 52 之间的牌,然后查看它是否已被选中,但我们也需要确保我们不会因为平庸的随机数函数而陷入无限循环。这通过跟踪您必须使用变量 errcount 进行猜测的次数来管理。阈值可以调整为允许每张牌更多或更少的猜测。我将其设置为 10 作为默认值。

您可以看到,使用数组使变量引用变得更加棘手。如前所示,设置值还不错,但是引用数组需要添加花括号,因此引用${deck[$randomcard]}是指数组 deck 中的 randomcard 槽位。

否则,不要让所有符号分散您的注意力,因为这是一个相当简单的循环。尝试 threshold 次,从数组 deck 中随机选择一张尚未被选中的牌(例如,其值已设置为零而不是初始值)。

此代码块的另一个有趣部分是 RANDOM 变量。每次您引用 $RANDOM 时,您都会自动获得一个介于零和 MAXINT(一个非常大的整数值)之间的不同数字,而无需初始化任何内容或进行任何特殊工作。通过键入以下内容亲自尝试一下echo $RANDOM在 Bourne Again Shell 命令提示符下。

这不是完整的代码段,因为我们还需要有一个备选方案,一段在随机猜测没有产生所需牌时使用的代码块,我们需要线性地遍历数组 deck 以找到可用的牌。通常,它只会在洗牌快结束时使用,那时只剩下几张牌。此代码如下所示

randomcard=1

while [ ${deck[$randomcard]} -eq 0 ]
do
   randomcard=$(( $randomcard + 1 ))
done

picked=$randomcard
deck[$picked]=0             # picked, remove it
return $picked

现在您已经熟悉数组,应该更容易阅读了。

本月我将在此停止,下个月我们将再次继续洗牌任务,包括解释如何将其制成 shell 函数并在主游戏脚本中使用它。敬请期待!

Dave Taylor 是 UNIX 领域 25 年的资深人士,Elm 邮件系统的创建者,也是畅销书 Wicked Cool Shell ScriptsTeach Yourself Unix in 24 Hours 等 16 部技术书籍的作者。他的主要网站是 www.intuitive.com

加载 Disqus 评论