剪刀石头布?

作者:Dave Taylor

在本文中,我将探讨一个极其复杂、变体多样的儿童游戏,并且编程任务将非常棘手。开玩笑的!剪刀石头布(或 RPS,正如它所知)非常容易模拟,因为实际上没有太多变体或可能的结果。

如果你以前从未玩过,这是一个一对一的游戏,每个人秘密选择三种可能的选项之一(石头、剪刀或布,你猜对了)。玩家同时展示他们的选择,然后有关于什么克制什么的规则。例如,剪刀克制布,因为“剪刀剪布”,石头克制剪刀,因为“石头砸剪刀”。如果双方玩家选择相同的选项,则为平局,游戏继续。

虽然你可以只玩一次,但通常也玩三局两胜制,以稍微平衡一下,尽管如果一切都是完全随机的,你将有 33.33% 的时间获胜。对于任何给定的选择,有 1/3 的几率会平局,即双方玩家都选择相同的东西,1/3 的几率你会赢,以及 1/3 的几率你会输。

世界剪刀石头布协会

然而,在真实的游戏中,事实证明也涉及到心理学。实际上,根据世界剪刀石头布协会的数据,石头被选择的次数占 35.4%,布占 35%,而剪刀仅占 29.6%。明白了吗?

然而,对于程序的第一个版本,让我们坚持完全随机的选择。在 Linux shell 脚本中选择 1 到 3 之间随机数的简单方法是使用 $RANDOM 变量,像这样


compchoice=$(( ($RANDOM % 3) + 1 ))

% 是取模运算符,它使随机整数除以 3,结果得到 0..2 的值。加一,你就得到了 1...3 的值。很简单。

使用一个简单的 shell 数组,你可以添加选择的名称(记住,数组从索引 0 开始)


declare -a RPS; RPS=(nothing rock paper scissors)

然后选择名称可以简单地指定为


choicename=${RPS[$compchoice]}

这三行代码对于一个微型脚本来说已经足够了,在这个脚本中,计算机可以在石头、剪刀和布之间随机选择


declare -a RPS; RPS=(nothing rock paper scissors)
compchoice=$(( ($RANDOM % 3) + 1 ))
echo "The computer chose ${RPS[$compchoice]}"
exit 0

简单,但不是很吸引人


$ sh rps.sh
The computer chose rock
$

让计算机提示用户选择,然后“选择”自己的选项并决定谁赢,这样更有趣。

将其变成游戏

通过提示用户使用数字值选择他们想要石头、剪刀还是布,可以轻松添加交互性。更好的是,你可以使用你在内部使用的相同数字值来提示他们


echo -n "Please choose (1 = rock / 2 = paper / 3 = scissors): "
read choice

添加交互性并不是一项特别繁重的任务,对吧?

现在你需要比较答案并生成结果消息。这最好在一个函数中完成,无论是独立的还是通过包含输出字符串并跟踪输赢。我将选择过度设计(当然),所以这是我的函数


results() {
   # output results of the game, increment wins if appropriate
   echo ""
   if [ $choice = $compchoice ] ; then
    echo "You both chose $choicename. TIED!"

   # rock beats scissors. paper beats rock. scissors beat paper.
   #  OR: 1 beats 3, 2 beats 1, and 3 beats 2.

   elif [ $choice -eq 1 -a $compchoice -eq 3 ] ; then
     echo "Your rock beats the computer's scissors! Huzzah!!"
     wins=$(( $wins + 1 ))
   elif [ $choice -eq 2 -a $compchoice -eq 1 ] ; then
     echo "Your paper beats the computer's rock! Hurray!"
     wins=$(( $wins + 1 ))
   elif [ $choice -eq 3 -a $compchoice -eq 2 ] ; then
     echo -n "Your scissors cut - and beat - the computer's "
     echo "paper! YAY!"
     wins=$(( $wins + 1 ))
   elif [ $choice -eq 3 -a $compchoice -eq 1 ] ; then
     echo "The computer's rock beats your scissors! Boo."
   elif [ $choice -eq 1 -a $compchoice -eq 2 ] ; then
     echo "The computer's paper beats your rock! Ptoi!"
   elif [ $choice -eq 2 -a $compchoice -eq 3 ] ; then
     echo -n "The computer's scissors cut - and beat - "
     echo "your paper! Bummer."
   else
     echo "Huh? choice=$choice and compchoice=$computer"
   fi
}

这很简单,只是需要大量输入。但实际上,这占了程序 95% 的工作。你所需要的只是一个循环机制,以便你“卡在”程序中,直到你厌倦了这个游戏——我的意思是准备结束。

请注意,上面的代码跟踪了获胜次数,但没有跟踪总游戏次数;这必须在主代码中完成,当然,由于大部分代码都被推入 results() 函数中,因此这非常简单


echo "Rock, paper, scissors..."
echo "(quit by entering 'q' to see your results)"
while [ true ] ; do
  echo ""
  echo -n "Choose (1 = rock / 2 = paper / 3 = scissors): "
  read choice
  if [ "$choice" = "q" -o "$choice" = "quit" -o -z "$choice" ]
  then
    echo ""
    echo "Done. You played $games games, and won $wins of 'em."
    exit 0
  fi
  compchoice=$(( ($RANDOM % 3) + 1 ))
  choicename=${RPS[$compchoice]}
  games=$(( $games + 1 ))
  results
done

快速运行表明,当游戏完全随机选择时,剪刀并不是一个糟糕的策略


$ sh rps.sh
Choose (1 = rock / 2 = paper / 3 = scissors): 3
Your scissors cut - and beat - the computer's paper! YAY!
$

当我尝试时,我得到了一个令人惊讶的长期结果:全剪刀策略产生了 50% 的胜率(12 局游戏中赢了 6 局)。如果计算机真的是随机选择的,那么统计上这是不太可能的,但有时随机并不是那么随机。

让我们看看选择布


$ sh rps.sh
Choose (1 = rock / 2 = paper / 3 = scissors): 2
The computer's scissors cut - and beat - your paper! Bummer.
$

事实上,在一个试验中,全部选择布只赢了 14 局游戏中的 4 局,而石头,最受欢迎的选择呢?那产生了 14 局游戏中 3 局的胜率——比布还差!

匹配概率

为了使这个程序更符合“真实”的选择统计数据,你可以做的最大改变是停止随机选择,而是反映剪刀石头布协会发布的百分比:石头被选择的次数占 35.4%,布占 35%,而剪刀仅占 29.6%。

对此进行建模的最简单方法是选择一个 1-1000 之间的随机数,然后说 1-354 是石头,355-705 是布,706-1000 是剪刀。与其用单行代码选择数字,不如编写一个函数,这非常容易。

你可以扩展的另一个领域是添加更多可能性,我敢打赌,大多数阅读本文的人都知道如何将“蜥蜴”和“斯波克”添加到游戏中。不确定?这里是如何玩五种对象 RPS 游戏的

就是这样。科学吗?不完全是。但是,嗯,剪刀石头布——拜托!

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

加载 Disqus 评论