65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的Yahtzee游戏

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (6投票s)

2004 年 10 月 26 日

5分钟阅读

viewsIcon

169242

downloadIcon

3038

一篇关于用 C# 创建一个简单的骰子游戏的文章。

引言

这是 Yahtzee 的一个简化版本,包含一个高分榜。希望您喜欢。

背景

这是我为 CodeProject 写的第二篇文章,希望对大家有所帮助,或者至少能带来一些乐趣。

有一天,我女儿问我能不能为电脑买个 Yahtzee。我想,与其买,为什么不自己写一个呢。她已经对它进行了相当充分的测试,所以希望在这个过程中大部分的 bug 都已经被修复了。我花了不少时间来写这个,如果你想了解这个程序是如何发展到现在的历史,可以访问我的网站

规则

Yahtzee 游戏的完整规则可以从Hasbro 的网站下载。

此版本的游戏只允许一个 Yahtzee 加分项,不包含 Joker 规则。

游戏

Yahtzee

该程序由四个窗体和两个相当有用的类组成,它们完成了大部分工作。其中一个类负责处理窗体顶部显示的骰子,而另一个类则负责检查分数。

骰子

dice 类继承自 System.Windows.Forms.UserControl,这样可以更轻松地绘制对象。该控件知道如何绘制自身以及下一个要绘制的数字。后者是通过 Random 类实现的,这最初带来了一些问题,例如如何确保两个或多个骰子不会用相同的种子初始化。我是通过以下方法实现的:

public void InitializeRandomRoll( int nSeed )
{
  DateTime aTime = new DateTime(1000);
  aTime = DateTime.Now;
  nSeed += (int)(aTime.Millisecond);
  RandomPick = new Random(nSeed);
}

由于初始种子始终不同,主窗体有自己的随机数生成器,并用下一个数字来播种上述方法。

Yahtzee 的另一个问题是,每轮有三次掷骰子。第一次,你掷所有五个骰子。然后,你选择一些骰子并把它们放在一边。其他的你放回骰盅,然后再次掷骰子。从第二次掷骰子开始,你可以选择保留其中的一些骰子,也可以不保留,然后掷剩下的骰子进行第三次掷骰。

我们需要能够锁定一个骰子,这样当按下掷骰按钮时它就不会被掷出。所以,我们添加一个变量来保存骰子的状态。

private bool m_bHoldState = false;
public bool HoldState
{
  get { return m_bHoldState; }
  set { m_bHoldState = value; }
}

现在,我们需要确保如果骰子被锁定,它就不会被掷出。这只需在 Roll() 方法中添加一个 if 语句即可。

public void Roll()
{
  // If the dice is not held, roll it
  if( !HoldState )
  {
    RollNumber = RandomPick.Next(1,7);
    this.Invalidate();
  }
}

最后,如果骰子未被锁定,则以黑色绘制;如果被锁定,则以红色绘制。以下方法在给定点绘制一个点

public void DrawDot( Graphics g, Point p )
{
  SolidBrush myBrush;

  if( HoldState )
  {
    myBrush = new SolidBrush( Color.Red );
  }
  else
  {
    myBrush = new SolidBrush( Color.Black );
  }

  g.FillEllipse( myBrush, p.X, p.Y, dotWidth, dotWidth );
  myBrush.Dispose();
}

其余的骰子代码都很容易理解。

计算分数

为了计算分数,创建了一个类。该类有许多计算单独分数的函数,它还保存总分、上半部分分数和下半部分分数的总和。

下面显示了在需要相同数字时,将五个骰子的总分数相加所使用的算法。所有相同数字的骰子的总和将返回给窗体,如果没有骰子与所需的数字相同,则返回零。

public int AddUpDice( int DieNumber, Dice[] myDice )
{
  int Sum = 0;

  for( int i = 0; i < 5; i++ )
  {
    if( myDice[i].RollNumber == DieNumber )
    {
      Sum += DieNumber;
    }
  }

  return Sum;
}

三条相同(Three of a Kind)和四条相同(Four of a Kind)的算法非常相似。它们都基于上述算法,但不知道三条或四条相同是指哪个数字。因此,使用两个循环来确定是否有三个或四个骰子是相同的。

public int CalculateThreeOfAKind( Dice[] myDice )
{
  int Sum = 0;

  bool ThreeOfAKind = false;

  for( int i = 1; i <= 6; i++ )
  {
    int Count = 0;
    for( int j = 0; j < 5; j++ )
    {
      if( myDice[j].RollNumber == i )
        Count++;

      if( Count > 2 )
        ThreeOfAKind = true;
    }
  }

  if( ThreeOfAKind )
  {
    for( int k = 0; k < 5; k++ )
    {
      Sum += myDice[k].RollNumber;
    }
  }

  return Sum;
}

计算满堂彩(Full House)的算法比前一个稍微复杂一些。其基础是,如果我们按升序对骰子数字进行排序,那么前两个骰子将是相同的,后三个骰子将是相同的,但第二个和第三个骰子必须不同。或者,前三个骰子是相同的,后两个骰子是相同的,但第三个和第四个骰子必须不同。这可以通过一个简单的 if 语句来实现。

我们不返回骰子的总和,而是返回一个固定的分数,即 25。

public int CalculateFullHouse( Dice[] myDice )
{
  int Sum = 0;

  int[] i = new int[5];

  i[0] = myDice[0].RollNumber;
  i[1] = myDice[1].RollNumber;
  i[2] = myDice[2].RollNumber;
  i[3] = myDice[3].RollNumber;
  i[4] = myDice[4].RollNumber;

  Array.Sort(i);

  if( (((i[0] == i[1]) && (i[1] == i[2])) && // Three of a Kind
       (i[3] == i[4]) && // Two of a Kind
       (i[2] != i[3])) ||
      ((i[0] == i[1]) && // Two of a Kind
       ((i[2] == i[3]) && (i[3] == i[4])) && // Three of a Kind
       (i[1] != i[2])) )
  {
    Sum = 25;
  }

  return Sum;
}

计算大顺子(Large Straight)的算法与计算满堂彩(Full House)的算法类似,但简单得多。 if 语句的基础是,当排序后,五个骰子包含数字 1、2、3、4 和 5,或者数字 2、3、4、5 和 6。其他任何组合都是不接受的。

同样,我们不返回骰子的总和,而是返回一个固定的分数,即 40。

public int CalculateLargeStraight( Dice[] myDice )
{
  int Sum = 0;

  int[] i = new int[5];

  i[0] = myDice[0].RollNumber;
  i[1] = myDice[1].RollNumber;
  i[2] = myDice[2].RollNumber;
  i[3] = myDice[3].RollNumber;
  i[4] = myDice[4].RollNumber;

  Array.Sort(i);

  if( ((i[0] == 1) &&
       (i[1] == 2) &&
       (i[2] == 3) &&
       (i[3] == 4) &&
       (i[4] == 5)) ||
      ((i[0] == 2) &&
       (i[1] == 3) &&
       (i[2] == 4) &&
       (i[3] == 5) &&
       (i[4] == 6)) )
  {
    Sum = 40;
  }

  return Sum;
}

计算小顺子(Small Straight)的算法比大顺子(Large Straight)算法更复杂。 if 语句的基础是,四个骰子必须包含数字序列中的数字,没有间隔。当有两个数字相同时,会出现问题。通过将相同的数字移动到数组的末尾并忽略它来解决这个问题。

同样,我们不返回骰子的总和,而是返回一个固定的分数,即 30。

public int CalculateSmallStraight( Dice[] myDice )
{
  int Sum = 0;

  int[] i = new int[5];

  i[0] = myDice[0].RollNumber;
  i[1] = myDice[1].RollNumber;
  i[2] = myDice[2].RollNumber;
  i[3] = myDice[3].RollNumber;
  i[4] = myDice[4].RollNumber;

  Array.Sort(i);

  // Problem can arise hear, if there is more than one of the same number, so
  // we must move any doubles to the end
  for( int j = 0; j < 4; j++ )
  {
    int temp = 0;
    if( i[j] == i[j+1] )
    {
      temp = i[j];

      for( int k = j; k < 4; k++ )
      {
        i[k] = i[k+1];
      }

      i[4] = temp;
    }
  }

  if( ((i[0] == 1) && (i[1] == 2) && (i[2] == 3) && (i[3] == 4)) ||
      ((i[0] == 2) && (i[1] == 3) && (i[2] == 4) && (i[3] == 5)) ||
      ((i[0] == 3) && (i[1] == 4) && (i[2] == 5) && (i[3] == 6)) ||
      ((i[1] == 1) && (i[2] == 2) && (i[3] == 3) && (i[4] == 4)) ||
      ((i[1] == 2) && (i[2] == 3) && (i[3] == 4) && (i[4] == 5)) ||
      ((i[1] == 3) && (i[2] == 4) && (i[3] == 5) && (i[4] == 6)) )
  {
    Sum = 30;
  }

  return Sum;
}

Yahtzee 的算法与三条相同(Three of a Kind)和四条相同(Four of a Kind)的算法相同。同样,我们不返回骰子的总和,而是返回一个固定的分数,即 50。

public int CalculateYahtzee( Dice[] myDice )
{
  int Sum = 0;

  for( int i = 1; i <= 6; i++ )
  {
    int Count = 0;
    for( int j = 0; j < 5; j++ )
    {
      if( myDice[j].RollNumber == i )
        Count++;

      if( Count > 4 )
        Sum = 50;
    }
  }

  return Sum;
}

在计算机会分数(Chance)时,我们实际上不需要做任何检查,只需将骰子相加并将其作为数字返回即可。

public int AddUpChance( Dice[] myDice )
{
  int Sum = 0;

  for( int i = 0; i < 5; i++ )
  {
    Sum += myDice[i].RollNumber;
  }

  return Sum;
}

计算总分

为了知道游戏何时结束,我们计算已经分配了多少分数。由于只有十四种可能的分数,我们知道何时停止。分数计数保存在以下变量中

private int ScoreCount = 0;

每次分配分数后,Reset 方法会检查游戏是否结束。

if( ScoreCount == 14 )
{
  DialogResult result;

  // Displays the MessageBox.
  result = MessageBox.Show( "Your Score is " + TotalScore.Text + ".
          Would You like to play again?", 
          "End Of Game", 
          MessageBoxButtons.YesNo, 
          MessageBoxIcon.Question );

  if( result == DialogResult.Yes )
  {
    // Reset Everything
    . . .
  }
  else
  {
    this.Close();
  }
}

祝您游戏愉快!

© . All rights reserved.