前言
最近喜欢上了买双色球,但是每次都不知道买啥号码比较好,所以心想着不如写个可以产生双色球号码的程序,每期都生成一个号码,再买这个号码.....
再加上最近在看敏捷软件开发这本书,所以打算用测试先行的方式写这个程序。
分析
双色球,大乐透什么的都是属于慈善项目。这类慈善项目的都会有个开奖号码,并且这个开奖号码都要符合一定的规则,所以先建个慈善项目的类:
public abstract class CharitableProject
{
public abstract int[] GenerateWinningNumbers()
public abstract bool[] CheckRules();
}
由于我们要研究的是双色球,而双色球属于慈善项目大家族的其中一项,双色球应该继承于慈善项目。
测试先行指的是,先不写具体实现逻辑,而是先写测试用例,然后为了通过这些测试用例,补充具体实现的过程。
public class DichroicBall : CharitableProject
{
public override int[] GenerateWinningNumbers()
{
return new int[0];
}
public override bool[] CheckRules(int[] num)
{
return new bool[0];
}
}
测试:【双色球中奖号码为7个】
public void TestMethod()
{
CharitableProject charitableProject = new DichroicBall();
var actual = charitableProject.GenerateWinningNumbers();
var expected = new int[7];
Assert.AreEqual(expected.Length, actual.Length);
}
运行此测试后,测试未能通过,所以得修改双色球的实现方法,使其产生7个中奖号码:
public class DichroicBall : CharitableProject
{
public override int[] GenerateWinningNumbers()
{
return new int[7];
}
}
修改之后再进行测试,测试通过了。但除了中奖号码是7个以外,号码的数值还有很多规则的。
所以希望可以自己添加规则,于是把测试用例改成以下的形式:
优化后的测试代码:
public void TestMethod()
{
CharitableProject.CharitableProject charitableProject = new DichroicBall();
var nums = charitableProject.GenerateWinningNumbers();
charitableProject.AddRules(DichroicBall.ThereAreSevenNumbers);
var actual = charitableProject.CheckRules(nums);
var expected = new bool[1] { true };
Assert.AreEqual(expected[0], actual[0]);
}
为了可以实现这种效果,需要使用委托,于是慈善项目类需要进行改变
public abstract class CharitableProject
{
public List<FuncRule> Roles = new List<FuncRule>();
public delegate void FuncRule();
public abstract int[] GenerateWinningNumbers();
public virtual bool[] CheckRules(int[] nums)
{
List<bool> roleResult = new List<bool>();
foreach (var role in Roles)
{
roleResult.Add(role(nums));
}
return roleResult.ToArray();
}
public virtual void AddRules(FuncRule rule)
{
Roles.Add(rule);
}
}
双色球类也要添加ThereAreSevenNumbers规则判断的方法:
public static bool ThereAreSevenNumbers(int[] nums)
{
return nums.Length == 7;
}
测试通过,所以继续添加几个规则:如下
public void TestMethod()
{
CharitableProject.CharitableProject charitableProject = new DichroicBall();
var nums = charitableProject.GenerateWinningNumbers();
charitableProject.AddRules(DichroicBall.ThereAreSevenNumbers);//一共7个数字
charitableProject.AddRules(DichroicBall.AllNumberBetween1And33);//全部号码在1到33之间
//前6位不重复
charitableProject.AddRules(DichroicBall.Rule_NoRepetitionOfTheFirstSixDigits);
//第7位在1到16之间
charitableProject.AddRules(DichroicBall.TheFinalNumberIsBetween1And16);
var actual = charitableProject.CheckRules(nums);
var expected = new bool[4] { true,true,true,true };
Assert.AreEqual(expected[0], actual[0], "7个数字");
Assert.AreEqual(expected[1], actual[1], "1~33");
Assert.AreEqual(expected[2], actual[2], "前6不重复");
Assert.AreEqual(expected[3], actual[3], "7位1~16");
}
在双色球类中补充完全对应的验证方法
public static bool ThereAreSevenNumbers(int[] nums)
{
return nums.Length == 7;
}
public static bool AllNumberBetween1And33(int[] nums)
{
foreach (var n in nums)
{
if (n < 1 || n > 33)
{
return false;
}
}
return true;
}
public static bool TheFinalNumberIsBetween1And16(int[] nums)
{
return nums[6] >= 1 && nums[6] <= 16;
}
public static bool Rule_NoRepetitionOfTheFirstSixDigits(int[] nums)
{
ISet<int> set = new HashSet<int>();
for (var i = 0; i < 6; i++)
{
set.Add(nums[i]);
}
return set.Count == 6;
}
运行测试,发现后三个不通过,现在开始写生成号码的代码....
public override int[] GenerateWinningNumbers()
{
Random random = new Random();
var number = new List<int>(33);
var result = new List<int>(7);
for (int i = 1; i <= 33; i++)
{
number.Add(i);
}
//产生6个白球
for (int i = 0; i < 6; i++)
{
int tempNum = random.Next(0, number.Count);
result.Add(number[tempNum]);
number.RemoveAt(tempNum);
}
result = result.OrderBy(d => d).ToList();//排序
result.Add(random.Next(1, 16));//随机产生篮球
return result.ToArray();
}
至此,所有的代码已经完成,开始测试下GenerateWinningNumbers方法生成的号码是否符合规则,运行测试:
测试通过。
完成的代码如下:
UnitTestDichroicBall.cs
[TestClass]
public class UnitTestDichroicBall
{
[TestMethod]
public void TestMethod()
{
CharitableProject.CharitableProject charitableProject = new DichroicBall();
var nums = charitableProject.GenerateWinningNumbers();
charitableProject.AddRules(DichroicBall.ThereAreSevenNumbers);//一共7个数字
//全部号码在1到33之间
charitableProject.AddRules(DichroicBall.AllNumberBetween1And33);
//前6位不重复
charitableProject.AddRules(DichroicBall.Rule_NoRepetitionOfTheFirstSixDigits);
//第7为在1到16之间
charitableProject.AddRules(DichroicBall.TheFinalNumberIsBetween1And16);
var actual = charitableProject.CheckRules(nums);
var expected = new bool[4] { true,true,true,true };
Assert.AreEqual(expected[0], actual[0], "7个数字");
Assert.AreEqual(expected[1], actual[1], "1~33");
Assert.AreEqual(expected[2], actual[2], "前6不重复");
Assert.AreEqual(expected[3], actual[3], "7位1到16");
}
}
CharitableProject.cs
public abstract class CharitableProject
{
public List<FuncRule> Roles = new List<FuncRule>();
public delegate bool FuncRule(int[] nums);
public abstract int[] GenerateWinningNumbers();
public virtual bool[] CheckRules(int[] nums)
{
List<bool> roleResult = new List<bool>();
foreach (var role in Roles)
{
roleResult.Add(role(nums));
}
return roleResult.ToArray();
}
public virtual void AddRules(FuncRule rule)
{
Roles.Add(rule);
}
}
DichroicBall.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace CharitableProject.Project
{
public class DichroicBall : CharitableProject
{
public override int[] GenerateWinningNumbers()
{
Random random = new Random();
var number = new List<int>(33);
var result = new List<int>(7);
for (int i = 1; i <= 33; i++)
{
number.Add(i);
}
//产生6个白球
for (int i = 0; i < 6; i++)
{
int tempNum = random.Next(0, number.Count);
result.Add(number[tempNum]);
number.RemoveAt(tempNum);
}
result = result.OrderBy(d => d).ToList();//排序
result.Add(random.Next(1, 16));//随机产生篮球
return result.ToArray();
}
public static bool ThereAreSevenNumbers(int[] nums)
{
return nums.Length == 7;
}
public static bool AllNumberBetween1And33(int[] nums)
{
foreach (var n in nums)
{
if (n < 1 || n > 33)
{
return false;
}
}
return true;
}
public static bool TheFinalNumberIsBetween1And16(int[] nums)
{
return nums[6] >= 1 && nums[6] <= 16;
}
public static bool Rule_NoRepetitionOfTheFirstSixDigits(int[] nums)
{
ISet<int> set = new HashSet<int>();
for (var i = 0; i < 6; i++)
{
set.Add(nums[i]);
}
return set.Count == 6;
}
}
}
完成之后再加个控制台程序:
class Program
{
static void Main(string[] args)
{
CharitableProject.CharitableProject charitableProject = new DichroicBall();
Console.WriteLine("输入需要产生的双色球号码数量,如:5");
var count = int.Parse(Console.ReadLine());
for (var i = 0; i < count; i++)
{
var nums = charitableProject.GenerateWinningNumbers();
foreach (var num in nums)
{
Console.Write(num);
Console.Write(" ");
}
Console.WriteLine();
}
Console.ReadLine();
}
}
一个可以生成双色球号码的经过测试验证的程序就完成了.....