remove the Generator hierarchy and the IGenerator interface

remove the Numbers class.

Limits are now passed to the constructor of each Lottery class.

{Generator}.Generate is now a static method.

Spin button renamed to Play button
This commit is contained in:
onyx-and-iris 2024-09-27 18:34:25 +01:00
parent 9e678865cd
commit ff4d77283c
6 changed files with 27 additions and 140 deletions

View File

@ -1,101 +1,19 @@
namespace Lottery
{
record Limits(int Count, int Lower, int Upper);
/// <summary>
/// Abstract base class for generators
/// All subclasses must implement the IGenerator interface
/// Provides some default implementation
/// </summary>
class Generator : IGenerator
{
public virtual Numbers Generate() => throw new NotImplementedException();
protected static void FillNumbers(List<int> NumberList, Limits Limits)
internal class Generator
{
public static List<int> Generate(Limits Limits)
{
List<int> Candidates = Enumerable.Range(Limits.Lower, Limits.Upper).ToList();
List<int> Numbers = [];
for (int i = 0; i < Limits.Count; i++)
{
int RandomIndex = Random.Shared.Next(Candidates.Count);
NumberList.Add(Candidates[RandomIndex]);
Numbers.Add(Candidates[RandomIndex]);
Candidates.RemoveAt(RandomIndex);
}
NumberList.Sort();
}
protected static void FillNumbers(
List<int> NormalList, Limits NormalLimits,
List<int> SpecialList, Limits SpecialLimits)
{
FillNumbers(NormalList, NormalLimits);
FillNumbers(SpecialList, SpecialLimits);
}
}
/// <summary>
/// Concrete UKLottoGenerator class.
/// Generates six balls from 1 to 59
/// </summary>
class UKLottoGenerator : Generator
{
private readonly Limits Limits = new(6, 1, 59);
public override Numbers Generate()
{
Numbers Numbers = new([]);
Generator.FillNumbers(Numbers.Normal, Limits);
return Numbers;
}
}
/// <summary>
/// Concrete EuroMillionsGenerator class.
/// Generates five balls from 1 to 50 and two balls from 1 to 12
/// </summary>
class EuroMillionsGenerator : Generator
{
private readonly Limits NormalLimits = new(5, 1, 50);
private readonly Limits SpecialLimits = new(2, 1, 12);
public override Numbers Generate()
{
NumbersWithSpecial Numbers = new([], []);
Generator.FillNumbers(Numbers.Normal, NormalLimits, Numbers.Special, SpecialLimits);
return Numbers;
}
}
/// <summary>
/// Concrete SetForLifeGenerator class.
/// Generates five balls from 1 to 47 and one ball from 1 to 10
/// </summary>
class SetForLifeGenerator : Generator
{
private readonly Limits NormalLimits = new(5, 1, 47);
private readonly Limits SpecialLimits = new(1, 1, 10);
public override Numbers Generate()
{
NumbersWithSpecial Numbers = new([], []);
Generator.FillNumbers(Numbers.Normal, NormalLimits, Numbers.Special, SpecialLimits);
return Numbers;
}
}
/// <summary>
/// Concrete ThunderBallGenerator class.
/// Generates fives balls from 1 to 39 and one ball from 1 to 14
/// </summary>
class ThunderBallGenerator : Generator
{
private readonly Limits NormalLimits = new(5, 1, 39);
private readonly Limits SpecialLimits = new(1, 1, 14);
public override Numbers Generate()
{
NumbersWithSpecial Numbers = new([], []);
Generator.FillNumbers(Numbers.Normal, NormalLimits, Numbers.Special, SpecialLimits);
Numbers.Sort();
return Numbers;
}
}

View File

@ -1,10 +0,0 @@
namespace Lottery
{
/// <summary>
/// All Generators must implement the Generate method
/// </summary>
interface IGenerator
{
Numbers Generate();
}
}

View File

@ -1,53 +1,36 @@
namespace Lottery
{
/// <summary>
/// Base Lottery class, use it for lotteries that generate a single set of numbers.
/// SpecialIdentifier may be overridden for Lotteries with Special values.
/// </summary>
/// <param name="Generator">A fully formed Generator</param>
internal class Lottery(IGenerator Generator)
internal class Lottery(Limits limits)
{
protected Numbers GenerateNumbers() => Generator.Generate();
public virtual string Output()
{
var numbers = GenerateNumbers();
return $"Numbers: {string.Join(", ", numbers.Normal)}";
}
protected Limits Limits => limits;
public virtual string Play() => $"Numbers: {string.Join(", ", Generator.Generate(Limits))}";
}
/// <summary>
/// Abstract base class, for lotteries with special values.
/// It subclasses Lottery.
/// </summary>
/// <param name="Generator">A fully formed Generator</param>
internal class LotteryWithSpecial(IGenerator Generator) : Lottery(Generator)
internal class LotteryWithSpecial(Limits limits, Limits specialLimits) : Lottery(limits)
{
protected Limits SpecialLimits => specialLimits;
protected virtual string SpecialIdentifier => throw new NotImplementedException();
public override string Output()
public override string Play()
{
var numbers = GenerateNumbers() as NumbersWithSpecial
?? throw new LotteryException($"Unable to generate numbers for {this}");
return string.Join("\t", [
$"Numbers: {string.Join(", ", numbers.Normal)}",
$"{SpecialIdentifier}: {string.Join(", ", numbers.Special)}",
$"Numbers: {string.Join(", ", Generator.Generate(Limits))}",
$"{SpecialIdentifier}: {string.Join(", ", Generator.Generate(SpecialLimits))}",
]);
}
}
internal class EuroMillionsLotteryWithSpecial(IGenerator Generator) : LotteryWithSpecial(Generator)
internal class EuroMillionsLottery(Limits limits, Limits specialLimits) : LotteryWithSpecial(limits, specialLimits)
{
protected override string SpecialIdentifier => "Lucky Stars";
}
internal class SetForLifeLotteryWithSpecial(IGenerator Generator) : LotteryWithSpecial(Generator)
internal class SetForLifeLottery(Limits limits, Limits specialLimits) : LotteryWithSpecial(limits, specialLimits)
{
protected override string SpecialIdentifier => "Life Ball";
}
internal class ThunderballLotteryWithSpecial(IGenerator Generator) : LotteryWithSpecial(Generator)
internal class ThunderballLottery(Limits limits, Limits specialLimits) : LotteryWithSpecial(limits, specialLimits)
{
protected override string SpecialIdentifier => "Thunderball";
}

View File

@ -9,9 +9,9 @@
Spacing="25">
<Picker x:Name="LotteryPicker" Title="Pick your lottery" SelectedIndexChanged="LotteryPicker_SelectedIndexChanged" />
<Label x:Name="NumbersLabel" Text="Click Spin button to generate" HorizontalOptions="Center" FontSize="Medium" />
<Label x:Name="NumbersLabel" Text="Click Play button to generate numbers" HorizontalOptions="Center" FontSize="Medium" />
<Button x:Name="SpinButton" Text="Spin" Clicked="SpinButton_Clicked"/>
<Button x:Name="PlayButton" Text="Play" Clicked="PlayButton_Clicked"/>
</VerticalStackLayout>
</ScrollView>

View File

@ -1,12 +1,14 @@
namespace Lottery
{
internal record Limits(int Count, int Lower, int Upper);
public partial class MainPage : ContentPage
{
readonly List<Lottery> Lotteries = [
new Lottery(new UKLottoGenerator()),
new EuroMillionsLotteryWithSpecial(new EuroMillionsGenerator()),
new SetForLifeLotteryWithSpecial(new SetForLifeGenerator()),
new ThunderballLotteryWithSpecial(new ThunderBallGenerator())
new Lottery(limits: new Limits(6, 1, 59)),
new EuroMillionsLottery(limits: new Limits(5, 1, 50), specialLimits: new Limits(2, 1, 12)),
new SetForLifeLottery(limits: new Limits(5, 1, 47), specialLimits: new Limits(1, 1, 10)),
new ThunderballLottery(limits: new Limits(5, 1, 39), specialLimits: new Limits(1, 1, 14))
];
const KindOfLottery DefaultLottery = KindOfLottery.Uk;
Lottery Lottery;
@ -22,9 +24,9 @@
Lottery = Lotteries[LotteryPicker.SelectedIndex];
}
private void SpinButton_Clicked(object sender, EventArgs e)
private void PlayButton_Clicked(object sender, EventArgs e)
{
NumbersLabel.Text = Lottery.Output();
NumbersLabel.Text = Lottery.Play();
SemanticScreenReader.Announce(NumbersLabel.Text);
}

View File

@ -1,6 +0,0 @@
namespace Lottery
{
internal record Numbers(List<int> Normal);
internal record NumbersWithSpecial(List<int> Normal, List<int> Special) : Numbers(Normal);
}