【问题标题】:Scalable solution for Rock-Paper-Scissor石头剪刀布的可扩展解决方案
【发布时间】:2012-03-04 07:06:55
【问题描述】:

刚刚体验了游戏的一个变种:Rock-Paper-Scissor-Lizard-Spock

我已经为传统的 R-P-S 问题编写了 Java 代码,但是当我尝试为新版本的游戏 (R-P-S-L-S) 扩展我的代码时......我觉得我的代码非常很糟糕。这是一个 sn-p :

 if (player1.equals("ROCK") && 
         player2.equals("SCISSORS")) {
        winner = 1;
    }
    // Paper covers rock...
    else if (player1.equals("PAPER") &&
         player2.equals("ROCK")) {
        winner = 1;
    }
    // Scissors cut paper...
    else if (player1.equals("SCISSORS") &&
         player2.equals("PAPER")) {
        winner = 1;
    }
    else {
        winner = 2;
    }

我意识到对于新版本以及超过 2 个玩家,代码无法轻松扩展。这主要是因为多个 if/else 或 switch/cases。我需要一些帮助重新设计我的代码以实现这两个目标:

  1. 根据 R-P-C-L-S 问题进一步修改。

  2. 支持超过 2 名玩家。

我不需要代码,只是一些指南应该会有所帮助。

谢谢!!

编辑:我认为这个游戏可以由超过 2 名玩家玩似乎是错误的。我很抱歉这个错误,请忽略第二个要求。

【问题讨论】:

  • 我对删除 if-else 块和硬编码数据非常感兴趣...
  • 谢谢大家 - 好答案太多了!
  • 绘制决策表。然后实现决策表。

标签: java algorithm


【解决方案1】:

在石头剪刀布游戏中,很容易使用循环中的索引来确定移动 a 是否胜过移动 b。因此,您不必像此处的其他答案所建议的那样,在代码中手动决定每种组合的结果。


对于 Rock-Paper-Scissor-Spock-Lizard 版本:

让我们为每个动作分配一个数字(0、1、2、3、4)。

请注意,每一步都胜过两步:

  1. 循环中前移(或前四格)
  2. 循环中前移两个案例

所以让d = (5 + a - b) % 5。那么:

  1. d = 1 或 d = 3 => 胜
  2. d = 2 或 d = 4 => b 获胜
  3. d = 0 => 并列

对于石头剪刀布版本:

d = (3 + a - b) % 3。那么:

  1. d = 1 => 一场胜利
  2. d = 2 => b 获胜
  3. d = 0 => 并列

泛化对于 n >= 3 和 n 奇数:

d = (n + a - b) % n。那么:

  1. 如果 d = 0 => 并列
  2. 如果 d % 2 = 1 => a 获胜
  3. 如果 d % 2 = 0 => b 获胜

【讨论】:

  • 这是迄今为止最优雅的解决方案。 :)
【解决方案2】:

Rock-Paper-Scissors 的本质是,您必须明确处理每种可能的状态组合的情况。因此,您必须涵盖的案例数量随着玩家数量呈指数增长,并且随着选项数量呈多项式(多项式的阶数为玩家数量)增加。

话虽如此,Java 的枚举很适合这种事情。

这是我的尝试:

import java.util.Arrays;
import java.util.List;

enum Result {
    WIN, LOSE, DRAW;
}

enum Option {

    ROCK(SCISSORS),
    PAPER(ROCK),
    SCISSORS(PAPER);

    private List<Option> beats;

    private Option(Option... beats) {
        this.beats = Arrays.asList(beats);
    }

    Result play(Option other) {
        if beats.contains(other) {
            return Result.WIN;
        } else if other.beats.contains(this) {
            return Result.LOSE;
        } else {
            return Result.DRAW;
        }
    }

}

因此,添加更多案例(Lizard 和 Spock)相对简单。添加更多玩家会更复杂;除此之外,您还必须确定三人剪刀石头布的规则是什么,因为我不知道。

【讨论】:

  • @sch True;我没想到。
  • 虽然这是最优雅的解决方案 - 它不会起作用。您不能在初始化选项之前引用它:(。您需要一个中间“设置”方法
【解决方案3】:

我认为:1 击败 2 或 5 输给其他人。 2 击败 3 或 1 输给其余的。 3 打 4 或 2 输给休息。 4 击败 5 或 3 输给其余的。 5 兽 1 或 3 输给其余的。对于 3 名玩家,比较 2 名玩家的值,然后比较获胜者和 3 号玩家。

【讨论】:

  • 这是不正确的。有 3 名玩家,每个玩家都可以战胜其他玩家。胜负关系不是关联的:A(摇滚)击败 B(剪刀),B(剪刀)击败 C(纸),但这并不意味着 A(摇滚)击败 C(纸)。
  • 正确。如果 A 击败 B 但 A 没有击败 C,则为平局。了解循环赛。 3 次中有 1 次没有赢家,但总有赢家 1 对 1
  • 如果有 2 名玩家投掷相同的球,他们只是 1 名玩家。如果 3 名玩家打出相同的平局
  • 让我们以我的例子和你的策略为例。 A 胜过 B,所以你将 A 与 C 进行比较? A 击败 B 得 1 分。C 击败 A 得 1 分。但 B 得 0 分,虽然他击败了 C。你只是忘了比较 B 和 C。
【解决方案4】:

设计一个枚举Choice(ROCK, PAPER, SCISSORS),其中每个枚举都有一个Set&lt;Choice&gt;,它会战胜它。

让你的每个玩家选择其中一个选项。

遍历你的玩家,对于每个玩家,遍历玩家列表中在他之后的所有其他玩家(对于玩家 0,遍历玩家 1、2、3 等;对于玩家 1,遍历玩家 2、3 等;...)。

对于每场比赛,你有三种可能性:

  1. A 击败 B(B 的选择在 A 击败的选项集中):增加 A 的分数
  2. A 和 B 有相同的选择:什么都不做
  3. A 没有击败 B:增加 B 的分数

【讨论】:

    【解决方案5】:

    我建议了一个更好的设计in an answer to another post。有一个开关,并切换每个可能的移动组合的单一编码,并且对于编码使用位置数字系统,其基数是 2 的幂,因此每个数字将直接映射到多个位,并且这样按位操作就很直观了。

    三个位足以满足五个选择,虽然八进制是理想的,但语法很烂,所以使用十六进制。然后,每个十六进制数字代表您的五个动作之一,并留有余地。一个字节足够大,两个编码两个玩家的同时移动,一个 int 代表 8 个,一个 long 代表 16 个。这很简单。按照链接获取代码示例。

    【讨论】:

      【解决方案6】:

      这是一个基本的逻辑问题。它足够小,您可以制作手动真值表(或跳到 k-map),最小化并获得解决方案。

      所以基本上,你需要先评估,如果是平局。然后,您需要评估相对于其他玩家的获胜情况。这样做而不需要与每个用户进行比较可能是一项令人困惑的任务。由于这只有 5 个变量,因此您可以使用 K-map 找到最小化的解决方案。

      您需要使用特定算法根据他们选择的项目来评估每个用户,以确定他们是否获胜。请注意,如果有 2 名以上的玩家,如果两个人选择相同的东西但都击败了第 3 名玩家,则可能会有多个获胜者。或者你可以认为这是一条领带,无论如何。我假设前者。您还应该检查所有玩家没有选择相同的项目。

      所以当您评估的用户选择“摇滚”时,我已经为您完成了算法的第一部分。

      在代码中,这看起来像:

      rock=0, paper=0, scissors=0, lizard=0, spock=0, win=0, tie=0
      if ( someone chose rock ) rock=1
      if ( someone chose paper ) paper=1
      if ( someone chose scissors ) scissors=1
      if ( someone chose lizard ) lizard=1
      if ( someone chose spock ) spock=1
      
      // Check if tie / draw, double check these, but I think I got them all
      tie=rock && !paper && spock && lizard || rock && paper && scissors ||  
          rock && paper && lizard || spock && paper && scissors || 
          spock && !rock && paper && lizard || !spock && scissors && lizard && paper
      
      if ( tie ) die()
      
      CheckIfUserWins() {
        if ( user chose rock ) {
          win=rock && !paper && !spock
        if ( user chose paper) {
          // ....  calculate using k-map and fill in
      
      }
      
      return win
      

      请注意,win=rock &amp;&amp; !paper &amp;&amp; !spock 正是根据您提供的链接上的图表所期望的。因此,您可以转到该图形并很快填写其余的方程式。

      这个解决方案不依赖于任何数量的玩家,除了说“有人选择了 X”。所以它应该扩展到> 5个玩家,等等。

      【讨论】:

        【解决方案7】:

        最短路径:

        var n = 5; // Rock, Paper, Scissors, Lizard-Spock
        
        function calculate(x, y, n) {
          return 1 - ((n + x - y) % n) % 2;
        }
        
        function getWinner(p1Gestrure, p2Guesture) {
          if(p1Gestrure === p2Guesture) {
             return - 1; // tie
          }
        
          return this.calculate(p1Gestrure, p2Guesture); // 0: win for p1. 1: win for p2.
        }
        

        我已经创建了一个 cli 游戏,请随意看看那里。 https://github.com/julianusti/rpc-es6

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-01-10
          • 2018-04-06
          • 1970-01-01
          • 1970-01-01
          • 2017-02-10
          • 2016-01-16
          • 1970-01-01
          相关资源
          最近更新 更多