Leo 正在与他的电脑进行一场友好的“石头、剪刀、布、蜥蜴、史波克”(rock-paper-scissors-lizard-Spock)游戏。游戏按轮进行。在每一轮中,Leo 和他的电脑同时从五个选项中做出选择:石头(rock)、布(paper)、剪刀(scissors)、蜥蜴(lizard)和史波克(Spock)。这五个选项中,每一个都克制另外两个选项,如图 F.1 所示。例如,石头克蜥蜴和剪刀,但被布和史波克克制。如果双方选择相同,则该轮平局。最终,每位玩家的得分是他们获胜的轮数。
图 F.1:游戏机制。图片由 VidTheKid 通过 Wikimedia Commons 提供
遗憾的是,Leo 的电脑并不是最聪明的,它只是遵循一种策略:在每一轮中从五个选项中均匀随机地选择一个。这使得游戏变得相当无聊,因为无论 Leo 采取什么策略,每位玩家预计都会赢得 40% 的轮数(且预计有 20% 的轮数是平局)。
我们提到过 Leo 的电脑智力不足吗?情况更糟:为了选择随机选项,Leo 的电脑使用了一个非常简单的线性同余生成器(LCG)。该 LCG 有三个参数:一个已知的质数 $p = 127$,以及两个固定但未知的整数 $0 \le a < p$ 和 $0 \le b < p$。此外,它还有一个状态,即一个整数 $0 \le x < p$。为了生成一个随机选项,Leo 的电脑首先根据以下规则更新状态:
$$x \leftarrow (a \cdot x + b) \pmod p$$
然后根据 $x \pmod 5$ 的值,按照下表选择五个选项之一:
| $x \pmod 5$ | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 选择的选项 | rock | paper | scissors | lizard | Spock |
从逻辑上讲,了解 Leo 的电脑如何选择随机数应该能让 Leo 在游戏中占据优势。但不幸的是 Leo 已经去世了,所以现在由你来完成这项工作。编写一个程序,在与 Leo 的电脑进行多轮游戏时,赢得至少 80% 的轮数。
交互
这是一个交互式问题,游戏按轮进行。在游戏开始前,会有一行输入,包含一个整数 $100 \le r \le 1000$,表示要进行的轮数。
然后,对于每一轮,你的程序应输出一行,包含“rock”、“paper”、“scissors”、“lizard”或“Spock”这五个字符串之一,表示你为下一轮做出的选择。输出该行后,请确保刷新标准输出。接着,将有一行输入,包含 Leo 的电脑在该轮中选择的选项名称,游戏进入下一轮。
提供了一个 Java jar 文件 SpockDebugger.jar 以帮助测试你的解决方案。在终端运行 java -jar SpockDebugger.jar 可获取使用信息。
样例
输入 1
500 scissors paper scissors rock scissors paper Spock scissors rock scissors ...
输出 1
Spock Spock Spock Spock Spock Spock Spock Spock Spock Spock ...
说明
假设 Leo 的电脑使用的参数为 $a = 17, b = 23$,初始状态为 $x = 42$。在第一轮中,状态更新为 $(17 \cdot 42 + 23) \pmod{127} = 102$,由于 $102 \pmod 5 = 2$,电脑做出的第一个动作为 scissors。在下一轮中,状态更新为 $(17 \cdot 102 + 23) \pmod{127} = 106$,导致动作为 paper。游戏的前 10 轮如下所示。在这个例子中,Leo 选择在每一轮都出 Spock,这似乎是一个出奇好的选择——在这 10 轮中,Leo 实际上赢了 7 轮(除了第二、第六和第七轮之外的所有轮次)——但 Leo 仍然没有赢得 80% 的轮数。通常情况下,这种策略对于击败电脑来说太天真了。
此示例中的额外换行符仅为清晰起见,以演示事件顺序。你在每次移动后应准确打印一个换行符,且输入中也不包含空行。