找到一种算法来平衡这个游戏

时间:2011-12-30 20:47:08

标签: algorithm

我试图解决我遇到问题的部分问题,这是我正在做的更大项目(不是家庭作业)的一部分。我认为将它描述为游戏是最容易的(因为我需要它来完成我正在努力的游戏)。这是一个单人游戏,就像我描述它一样。

START 您从以下两个职位之一开始:

  • 重量(2,0,-1)和一个red
  • 重量(3,1,-1)和两个red

结束当您没有更多游戏时,游戏结束。目标是以权重(0,0,0)结束游戏。

有两种类型的游戏redblue。给定一个游戏,你可以选择以下四个部分中的一个:A,B,C,D,它可以为你提供额外的重量,也可能是额外的游戏。以下是规则:

  • red游戏:

    • A增加了重量(0,-2,-1)
    • B会增加权重(1,-1,-1)并添加一个red游戏
    • C会增加重量(2,0,-1)和两个red
    • D会增加重量(0,2,1)和两个blue

blue次播放的规则类似,但重量的前两列是交换的,最后一列是反向的,播放的类型是相反的,所以你得到了这个:

  • blue游戏:

    • A增加了重量(-2,0,1)
    • B会增加权重(-1,1,1)并添加一个blue游戏
    • C会增加重量(0,2,1)和两个blue
    • D会增加重量(2,0,-1)和两个red

问题可以赢得这场比赛吗?

我尝试编写一个通过选择游戏来赢得游戏的程序,以便在没有更多游戏时最终余额为(0,0,0)。只有我似乎无法做到这一点。所以现在我认为也许没有算法来赢得比赛。我真的很想知道为什么会这样。如果有人有任何想法我可以证明这一点,那么请让我知道!或者,如果你尝试并找到获胜的方法,那么请告诉我。谢谢!

3 个答案:

答案 0 :(得分:11)

可能我错过了一些东西,但只是通过检查,看起来这一系列步骤应该有效吗? :

  • 以“重量(2,0,-1)和一个red玩”
  • 开头
  • 进行red游戏,C,“增加重量(2,0,-1)和两个red游戏”,为您留下重量(4,0,-2)和两个{ {1}}播放。
  • 进行red游戏,red,“增加重量A”,让您体重(0,-2,-1)(4,-2,-3)游戏。
  • 进行red游戏,red,“增加重量D和两个(0,2,1)游戏”,为您留下重量blue和两个{ {1}}播放。
  • 进行(4,0,-2)游戏,blue,“增加重量blue”,让您体重A(-2,0,1)游戏。
  • 进行(2,0,-1)游戏,blue,“增加重量blue”,为您留下重量A而不进行游戏。

更具示意性:

(-2,0,1)

。 。 。没有?


已编辑添加:

我是如何找到这个:

由于这个问题引起了很多人的兴趣,也许我应该解释一下我是如何找到上述解决方案的。这基本上是运气;我碰巧发现了两个关键的观察结果:

  • (0,0,0)move weight plays ------ --------- ------- (2,0,-1) red red C (4,0,-2) red x2 red A (4,-2,-3) red red D (4,0,-2) blue x2 blue A (2,0,-1) blue blue A (0,0,0) - 在重量方面相互抵消(前者添加red A,后者添加red D),并添加总共两个{{1}播放(来自(0,-2,-1))和没有(0,2,1)播放;因此,如果你一个接一个地播放,你可以将两个blue个剧本“转换”为两个red D个剧本。
  • red取消了初始权重(它添加red)并且不添加任何播放,因此可以通过将一个blue播放“转换为”{{1}来解决整个问题播放。

这给了我一个良好的开端。我从blue A开始,以便获得我可以“转换”为两个(2,0,-1)次播放的两个red次播放,我立即看到blue也是“对面“red C权重,因此也可以red取消。在我的脑海中,这一切似乎完全抵消了;然后我把它写下来以确保。

证明它是最小的:

此外,虽然我当时没有理由通过它,但我也可以证明这是一个“最小”的起始位置的获胜序列 - 我的意思是,如果一个序列以“重量”开头bluered C播放“并以重量blue A结尾且没有播放,则序列必须包含至少五个移动。为此,假设我们有一个满足此条件的序列,并且少于而不是五个移动。然后:

  1. 由于重量的第一个分量开始为正,并且没有blue A比赛减少它,序列将需要至少一个(2,0,-1)比赛,这意味着它必然包括{{1}至少一次(因为这是获得red游戏的唯一途径,如果你不开始一个)。
  2. 由于序列至少包含(0,0,0)一次,而red为我们提供了两次blue次播放,因此序列必须包含red D至少两次(因为游戏不会结束直到没有戏剧为止。
  3. 由于序列至少包含blue一次,而red D为权重的第二个分量添加2,因此它必须至少包含red D一次,否则包含{ {1}}至少两次(因为没有其他方法可以将重量的第二个分量减少至少2)。但显然,如果它至少包含blue一次,blue至少两次,red D至少两次,那么它将包含至少五次移动,这是假设禁止的;所以它必须使用red D方法。
  4. 因此,序列至少包含red A一次,red B至少两次,red D至少一次。并且通过假设,它包含少于五个移动。因此,它必须包含完全一个blue,两个red B,一个red A和其他零个动作。
  5. 起始位置只给我们一个red D个游戏,序列中恰好包含两个blue个游戏。因此,序列必须包含一个只能产生red A个游戏的移动。但唯一可能的做法是red D,序列不包含。
  6. 因此,没有这样的序列是可能的。

    其他起始位置:

    我还可以使用类似的参数证明任何以“weight blue和两个red A播放”选项开头的解决方案也必须包含至少五个动作。一个这样的五步解决方案是:

    red

答案 1 :(得分:6)

以下是每个起始位置的解决方案:

Start 1: (2, 0, -1)  Reds=1  Blues=0
Red B ==> (3, -1, -2)  Reds=1  Blues=0
Red B ==> (4, -2, -3)  Reds=1  Blues=0
Red D ==> (4, 0, -2)  Reds=0  Blues=2
Blue A ==> (2, 0, -1)  Reds=0  Blues=1
Blue A ==> (0, 0, 0)  Reds=0  Blues=0

Start 2: (3, 1, -1)  Reds=2  Blues=0
Red A ==> (3, -1, -2)  Reds=1  Blues=0
Red B ==> (4, -2, -3)  Reds=1  Blues=0
Red D ==> (4, 0, -2)  Reds=0  Blues=2
Blue A ==> (2, 0, -1)  Reds=0  Blues=1
Blue A ==> (0, 0, 0)  Reds=0  Blues=0

通过以下随机游走的C#程序立即找到,并在少量移动后放弃。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SO8683939
{
  struct State
  {
    public int V1;
    public int V2;
    public int V3;
    public int Reds;
    public int Blues;
    public int Tokens { get { return Reds + Blues; } }
    public string Description;
    public State(int v1, int v2, int v3, int reds, int blues)
    {
      V1 = v1;
      V2 = v2;
      V3 = v3;
      Reds = reds;
      Blues = blues;
      Description = null;
    }
    public State Add(State other)
    {
      State sum;
      sum.V1 = V1 + other.V1;
      sum.V2 = V2 + other.V2;
      sum.V3 = V3 + other.V3;
      sum.Reds = Reds + other.Reds;
      sum.Blues = Blues + other.Blues;
      sum.Description = null;
      return sum;
    }
    public override string ToString()
    {
      var detail = string.Format("({0}, {1}, {2})  Reds={3}  Blues={4}", V1, V2, V3, Reds, Blues);
      if (Description != null)
      {
        return Description + ": " + detail;
      }
      return detail;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var start1 = new State(2, 0, -1, 1, 0) { Description = "Start 1" };
      var start2 = new State(3, 1, -1, 2, 0) { Description = "Start 2" };

      var end = new State(0, 0, 0, 0, 0);

      var redA = new State(0, -2, -1, -1, 0) { Description = "Red A" };
      var redB = new State(1, -1, -1, 0, 0) { Description = "Red B" }; ;
      var redC = new State(2, 0, -1, 1, 0) { Description = "Red C" }; ;
      var redD = new State(0, 2, 1, -1, 2) { Description = "Red D" }; ;
      var redOptions = new[] { redA, redB, redC, redD };

      var blueA = new State(-2, 0, 1, 0, -1) { Description = "Blue A" };
      var blueB = new State(-1, 1, 1, 0, 0) { Description = "Blue B" };
      var blueC = new State(0, 2, 1, 0, 1) { Description = "Blue C" };
      var blueD = new State(2, 0, -1, 2, -1) { Description = "Blue D" };
      var blueOptions = new[] { blueA, blueB, blueC, blueD };

      var startingPosition = start1;
      var maxSolutionLength = 5;

      var rand = new Random();
      var path = new List<State>();
      while (true)
      {
        var current = startingPosition;
        path.Clear();
        //Console.WriteLine("Starting");
        //Console.WriteLine(current);
        while (true)
        {
          State selected;
          if (current.Reds == 0)
          {
            selected = blueOptions[rand.Next(4)];
          }
          else if (current.Blues == 0)
          {
            selected = redOptions[rand.Next(4)];
          }
          else
          {
            if (rand.NextDouble() < 0.5)
            {
              selected = blueOptions[rand.Next(4)];
            }
            else
            {
              selected = redOptions[rand.Next(4)];
            }
          }
          //Console.WriteLine(selected);
          path.Add(selected);
          current = current.Add(selected);
          //Console.WriteLine(current);
          if (current.Equals(end))
          {
            Console.WriteLine("Success!");
            var retrace = startingPosition;
            Console.WriteLine(retrace);
            foreach (var selection in path)
            {
              retrace = retrace.Add(selection);
              Console.WriteLine("{0} ==> {1}", selection.Description, retrace);
            }
            Console.ReadLine();
            break;
          }
          else if (current.Tokens == 0)
          {
            // fail
            //Console.WriteLine("Fail");
            break;
          }
          else if (path.Count >= maxSolutionLength)
          {
            // fail
            //Console.WriteLine("Fail");
            break;
          }
        }
      }
    }
  }
}

答案 2 :(得分:2)

将红色和蓝色游戏的数量视为您位置的两个额外维度。这大大降低了问题的复杂性。

下面,问题以最后两个维度重新表示,表示剩余的红色和蓝色游戏。

START 您从以下两个职位之一开始:

  • 体重(2, 0,-1, 1, 0)
  • 体重(3,-1,-1, 2, 0)

结束当你有体重(0, 0, 0, 0, 0)时游戏结束。

考虑到一个游戏,你可以从八个部分中选择一个:Ar,Br,Cr,Dr,Ab,Bb,Cb,Db来增加你的体重。以下是规则:

  • Ar增加了重量(0,-2,-1,-1, 0)
  • Br增加了重量(1,-1,-1, 0, 0)
  • Cr增加了重量(2, 0,-1, 1, 0)
  • Dr增加了重量(0, 2, 1,-1, 2)
  • Ab增加了重量(-2,0, 1, 0,-1)
  • Bb增加了重量(-1,1, 1, 0, 0)
  • Cb增加了重量(0, 2, 1, 0, 1)
  • Db增加了重量(2, 0,-1, 2,-1)

此外,最后两个维度可能永远不会是负面的。

现在,它可以作为一个8变量方程来解决。