diff --git a/FitNesseRoot/OnlineLottery/SettlementTestSuite.wiki b/FitNesseRoot/OnlineLottery/SettlementTestSuite.wiki new file mode 100644 index 0000000..1df4b2f --- /dev/null +++ b/FitNesseRoot/OnlineLottery/SettlementTestSuite.wiki @@ -0,0 +1 @@ +!contents -R \ No newline at end of file diff --git a/FitNesseRoot/OnlineLottery/SettlementTestSuite/OneWinnerSixBalls.wiki b/FitNesseRoot/OnlineLottery/SettlementTestSuite/OneWinnerSixBalls.wiki new file mode 100644 index 0000000..559c9ac --- /dev/null +++ b/FitNesseRoot/OnlineLottery/SettlementTestSuite/OneWinnerSixBalls.wiki @@ -0,0 +1,22 @@ +--- +Test +--- +|Tickets in the Draw| +|Player|Numbers|Value| +|Ford|2,11,22,33,39,18|50| +|Arthur|1,5,4,7,9,20|50| +|Trisha|10,21,30,6,16,26|50| +|Marvin|12,13,14,15,16,17|50| + +|Draw results are|1,5,4,20,9,7| + +|Prize Distribution| +|Winning Tickets?|Prize Money?| +|1 |68 | + +|Accounts after the Draw| +|Player|Balance?| +|Arthur|118| +|Ford|50| +|Trisha|50| +|Marvin|50| \ No newline at end of file diff --git a/FitNesseRoot/OnlineLottery/SettlementTestSuite/SetUp.wiki b/FitNesseRoot/OnlineLottery/SettlementTestSuite/SetUp.wiki new file mode 100644 index 0000000..45f3f54 --- /dev/null +++ b/FitNesseRoot/OnlineLottery/SettlementTestSuite/SetUp.wiki @@ -0,0 +1,8 @@ +!|OnlineLottery.Test.SettlementTest| + +|Accounts before the draw| +|player|balance| +|Arthur|100| +|Ford|100| +|Trisha|100| +|Marvin|100| \ No newline at end of file diff --git a/FitNesseRoot/OnlineLottery/SettlementTestSuite/TwoWinnersFourBalls.wiki b/FitNesseRoot/OnlineLottery/SettlementTestSuite/TwoWinnersFourBalls.wiki new file mode 100644 index 0000000..21dcc40 --- /dev/null +++ b/FitNesseRoot/OnlineLottery/SettlementTestSuite/TwoWinnersFourBalls.wiki @@ -0,0 +1,22 @@ +--- +Test +--- +|Tickets in the Draw| +|Player|Numbers|Value| +|Ford|2,11,22,33,39,18|50| +|Arthur|1,5,4,7,9,20|80| +|Trisha|10,1,20,5,4,11|20| +|Marvin|12,13,14,15,16,17|50| + +|Draw results are|1,5,4,20,38,37| + +|Prize Distribution| +|Winning Tickets?|Prize Money?| +|2 |2, 8 | + +|Accounts after the Draw| +|Player|Balance?| +|Arthur|28| +|Ford|50| +|Trisha|82| +|Marvin|50| \ No newline at end of file diff --git a/FitNesseRoot/OnlineLottery/TicketReview.wiki b/FitNesseRoot/OnlineLottery/TicketReview.wiki new file mode 100644 index 0000000..70df22d --- /dev/null +++ b/FitNesseRoot/OnlineLottery/TicketReview.wiki @@ -0,0 +1,4 @@ +--- +Suite +--- +!contents -R \ No newline at end of file diff --git a/FitNesseRoot/OnlineLottery/TicketReview/SetUp.wiki b/FitNesseRoot/OnlineLottery/TicketReview/SetUp.wiki new file mode 100644 index 0000000..5c4854c --- /dev/null +++ b/FitNesseRoot/OnlineLottery/TicketReview/SetUp.wiki @@ -0,0 +1,5 @@ +!|OnlineLottery.Test.ReviewTickets| + +|Draw on |01.01.2017| is open| + +|Player|john|opens account with|100|dollars| \ No newline at end of file diff --git a/FitNesseRoot/OnlineLottery/TicketReview/SeveralTicketsOneDraw.wiki b/FitNesseRoot/OnlineLottery/TicketReview/SeveralTicketsOneDraw.wiki new file mode 100644 index 0000000..f08df41 --- /dev/null +++ b/FitNesseRoot/OnlineLottery/TicketReview/SeveralTicketsOneDraw.wiki @@ -0,0 +1,14 @@ +--- +Test +--- +|Player|john|buys a ticket with numbers|1,3,4,5,8,10|for draw on|01.01.2017| + +|Player|john|buys a ticket with numbers|2,4,5,8,10,12|for draw on|01.01.2017| + +|Player|john|buys|5|tickets with numbers|3,6,9,12,15,18|for draw on|01.01.2017| + +|Player|john|lists open tickets| +|Drawdate|Numbers|Value| +|01.01.2017|1,3,4,5,8,10|10| +|01.01.2017|2,4,5,8,10,12|10| +|01.01.2017|3,6,9,12,15,18|50| \ No newline at end of file diff --git a/FitNesseRoot/OnlineLottery/TicketReview/SeveralTicketsTwoDraws.wiki b/FitNesseRoot/OnlineLottery/TicketReview/SeveralTicketsTwoDraws.wiki new file mode 100644 index 0000000..b18f9ed --- /dev/null +++ b/FitNesseRoot/OnlineLottery/TicketReview/SeveralTicketsTwoDraws.wiki @@ -0,0 +1,24 @@ +--- +Test +--- +|Draw on|02.01.2017|is open| + +|Draw on|03.01.2017|is open| + +|Player|john|buys a ticket with numbers|1,3,4,5,8,10|for draw on|02.01.2017| + +|Player|john|buys a ticket with numbers|1,3,4,5,8,10|for draw on|01.01.2017| + +|Player|john|buys|5|tickets with numbers|3,6,9,12,15,18|for draw on|01.01.2017| + +|Player|john|lists tickets for draw on|01.01.2017| +|value|numbers| +|10|1,3,4,5,8,10| +|50|3,6,9,12,15,18| + +|Player|john|lists tickets for draw on|02.01.2017| +|value|numbers| +|10|1,3,4,5,8,10| + +|Player|john|lists tickets for draw on|03.01.2017| +|value|numbers| \ No newline at end of file diff --git a/FitNesseRoot/OnlineLottery/TicketReview/TwoAccountsOneDraw.wiki b/FitNesseRoot/OnlineLottery/TicketReview/TwoAccountsOneDraw.wiki new file mode 100644 index 0000000..dd7a124 --- /dev/null +++ b/FitNesseRoot/OnlineLottery/TicketReview/TwoAccountsOneDraw.wiki @@ -0,0 +1,16 @@ +--- +Test +--- +|Player|tom|opens account with|50|dollars| + +|Player|john|buys a ticket with numbers|1,3,4,5,8,10|for draw on|01/01/2017| + +|Player|tom|buys a ticket with numbers|2,4,5,8,10,12|for draw on|01/01/2017| + +|Player|john|lists tickets for draw on|01/01/2017| +|value|numbers| +|10|1,3,4,5,8,10| + +|Player|tom|lists tickets for draw on|01/01/2017| +|value|numbers| +|10|2,4,5,8,10,12| \ No newline at end of file diff --git a/FitNesseRoot/OnlineLottery/TicketReview/WinningsRecordedCorrectly.wiki b/FitNesseRoot/OnlineLottery/TicketReview/WinningsRecordedCorrectly.wiki new file mode 100644 index 0000000..04a6b05 --- /dev/null +++ b/FitNesseRoot/OnlineLottery/TicketReview/WinningsRecordedCorrectly.wiki @@ -0,0 +1,21 @@ +--- +Test +--- +|Draw on|02.01.2017|is open| + +|Player|john|buys a ticket with numbers|1,3,4,5,8,10|for draw on|01.01.2017| + +|Player|john|buys a ticket with numbers|1,3,4,5,8,10|for draw on|02.01.2017| + +|Player|john|buys|5|tickets with numbers|3,6,9,12,15,18|for draw on|01.01.2017| + +|Numbers|1,3,4,5,31,32|are drawn on|01.01.2017| + +|Player|john|lists tickets for draw on|01.01.2017| +|value|numbers|is open|winnings| +|10|1,3,4,5,8,10|false|3| +|50|3,6,9,12,15,18|false|0| + +|Player|john|lists open tickets| +|drawdate|value|numbers| +|02.01.2017|10|1,3,4,5,8,10| diff --git a/FitNesseRoot/RecentChanges.wiki b/FitNesseRoot/RecentChanges.wiki index 8f22cf7..2bc32eb 100644 --- a/FitNesseRoot/RecentChanges.wiki +++ b/FitNesseRoot/RecentChanges.wiki @@ -1,3 +1,13 @@ +|OnlineLottery.TicketReview.SeveralTicketsOneDraw||23:18:36 Mo, Apr 10, 2017| +|OnlineLottery.TicketReview.WinningsRecordedCorrectly||22:36:05 Mo, Apr 10, 2017| +|OnlineLottery.TicketReview.SeveralTicketsTwoDraws||22:19:41 Mo, Apr 10, 2017| +|OnlineLottery.TicketReview.TwoAccountsOneDraw||22:05:37 Mo, Apr 10, 2017| +|OnlineLottery.TicketReview.SetUp||21:11:28 Mo, Apr 10, 2017| +|OnlineLottery.TicketReview||21:09:28 Mo, Apr 10, 2017| +|OnlineLottery.SettlementTestSuite.OneWinnerSixBalls||24:49:38 Mo, Apr 10, 2017| +|OnlineLottery.SettlementTestSuite.TwoWinnersFourBalls||24:49:01 Mo, Apr 10, 2017| +|OnlineLottery.SettlementTestSuite.SetUp||22:16:55 So, Apr 09, 2017| +|OnlineLottery.SettlementTestSuite||22:13:42 So, Apr 09, 2017| |OnlineLottery.PurchaseTicketTestSuite.NotEnoughMoney||22:14:46 Do, Apr 06, 2017| |OnlineLottery.PurchaseTicketTestSuite.NegativeAmountOfTickets||22:14:26 Do, Apr 06, 2017| |OnlineLottery.PurchaseTicketTestSuite.MoreThanSixNumbers||22:14:04 Do, Apr 06, 2017| diff --git a/FitNesseRoot/properties b/FitNesseRoot/properties index 4707b0f..f971cee 100644 --- a/FitNesseRoot/properties +++ b/FitNesseRoot/properties @@ -1,3 +1,3 @@ ##FitNesse properties -#Thu Apr 06 21:36:03 CEST 2017 -Version=vUnknown +#Mon Apr 10 18:54:43 CEST 2017 +Version=v20161106 diff --git a/OnlineLottery/DrawManager.cs b/OnlineLottery/DrawManager.cs index 56ca913..a13fcdd 100644 --- a/OnlineLottery/DrawManager.cs +++ b/OnlineLottery/DrawManager.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; namespace OnlineLottery { public class DrawManager : IDrawManager { + public decimal OperatorDeductionFactor => 0.5m; + private readonly IPlayerManager _playerManager; private readonly Dictionary _draws = new Dictionary(); @@ -28,13 +31,69 @@ public IDraw CreateDraw(DateTime drawDate) public void PurchaseTicket(DateTime drawDate, int playerId, int[] numbers, decimal value) { if (!_draws.ContainsKey(drawDate)) throw new DrawNotOpenException(); - if(numbers.Length != 6) throw new WrongAmountOfNumbersException(); - if(value < 0) throw new InvalidPurchaseException(); + if (numbers.Length != 6) throw new WrongAmountOfNumbersException(); + if (value < 0) throw new InvalidPurchaseException(); var d = _draws[drawDate]; var player = _playerManager.GetPlayer(playerId); _playerManager.AdjustBalance(playerId, -value); d.AddTicket(new Ticket(player, drawDate, numbers, value)); } + + public IList GetOpenTickets(int playerId) + => (from draw in _draws + from ticket in draw.Value.Tickets + where ticket.Holder.PlayerId == playerId && ticket.IsOpen + select ticket).ToList(); + + public IList GetTickets(DateTime drawDate, int playerId) + => (from draw in _draws + from ticket in draw.Value.Tickets + where ticket.Holder.PlayerId == playerId && ticket.DrawDate == drawDate + select ticket).ToList(); + + public void SettleDraw(DateTime drawDate, int[] results) + { + var d = _draws[drawDate]; + d.IsOpen = false; + var ticketCategories = SplitTicketsIntoCategories(results, d.Tickets); + for (var commonNumbers = 0; commonNumbers <= results.Length; commonNumbers++) + SettleTicketCategories(commonNumbers, d, ticketCategories[commonNumbers]); + } + + private void SettleTicketCategories(int commonNumbers, IDraw draw, List tickets) + { + var prizePool = new WinningsCalculator().GetPrizePool(commonNumbers, draw.TotalPoolSize * (1 - OperatorDeductionFactor)); + var totalTicketValue = GetTotalTicketValue(tickets); + foreach (Ticket t in tickets) + SettleTicket(t, prizePool, totalTicketValue); + } + + private void SettleTicket(Ticket t, decimal prizePool, decimal totalTicketValue) + { + t.IsOpen = false; + if (prizePool <= 0) return; + + t.Winnings = t.Value * prizePool / totalTicketValue; + _playerManager.AdjustBalance(t.Holder.PlayerId, t.Winnings); + } + + private static Dictionary> SplitTicketsIntoCategories(int[] results, IEnumerable tickets) + { + var ticketCategories = new Dictionary>(); + + for (var i = 0; i <= results.Length; i++) + ticketCategories[i] = new List(); + + foreach(var t in tickets) + ticketCategories[CountCommonElements(t.Numbers, results)].Add(t); + + return ticketCategories; + } + + private static int CountCommonElements(IEnumerable ticketNumbers, int[] results) + => ticketNumbers.Sum(ticketNumber => results.Count(result => result == ticketNumber)); + + private static decimal GetTotalTicketValue(IEnumerable tickets) => tickets.Sum(t => t.Value); } } \ No newline at end of file diff --git a/OnlineLottery/IDrawManager.cs b/OnlineLottery/IDrawManager.cs index e1ababb..f1dd27a 100644 --- a/OnlineLottery/IDrawManager.cs +++ b/OnlineLottery/IDrawManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace OnlineLottery { @@ -22,5 +23,9 @@ public interface IDrawManager IDraw GetDraw(DateTime date); IDraw CreateDraw(DateTime drawDate); void PurchaseTicket(DateTime drawDate, int playerId, int[] numbers, decimal value); + void SettleDraw(DateTime drawDate, int[] results); + IList GetOpenTickets(int playerId); + decimal OperatorDeductionFactor { get; } + IList GetTickets(DateTime drawDate, int playerId); } } \ No newline at end of file diff --git a/OnlineLottery/ITicket.cs b/OnlineLottery/ITicket.cs index 3739929..f9103e5 100644 --- a/OnlineLottery/ITicket.cs +++ b/OnlineLottery/ITicket.cs @@ -8,5 +8,7 @@ public interface ITicket IPlayerInfo Holder { get; } DateTime DrawDate { get; } decimal Value { get; } + bool IsOpen { get; } + decimal Winnings { get; } } } \ No newline at end of file diff --git a/OnlineLottery/OnlineLottery.csproj b/OnlineLottery/OnlineLottery.csproj index b5288fe..442238d 100644 --- a/OnlineLottery/OnlineLottery.csproj +++ b/OnlineLottery/OnlineLottery.csproj @@ -52,6 +52,8 @@ + + diff --git a/OnlineLottery/Test/ReviewTickets.cs b/OnlineLottery/Test/ReviewTickets.cs new file mode 100644 index 0000000..d7abf0e --- /dev/null +++ b/OnlineLottery/Test/ReviewTickets.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using fitlibrary; + +namespace OnlineLottery.Test +{ + public class ReviewTickets : DoFixture + { + private readonly IDrawManager _drawManager; + private readonly IPlayerManager _playerManager; + + public ReviewTickets() + { + _playerManager = new PlayerManager(); + _drawManager = new DrawManager(_playerManager); + } + + public void DrawOnIsOpen(DateTime drawDate) => _drawManager.CreateDraw(drawDate); + + public void PlayerOpensAccountWithDollars(string username, decimal balance) + => _playerManager.AdjustBalance(_playerManager.RegisterPlayer(new PlayerRegistrationInfo + { + Username = username, + Name = username, + Password = "XXXXXX" + }), balance); + + public void PlayerBuysATicketWithNumbersForDrawOn(string username, int[] numbers, DateTime drawDate) + => PlayerBuysTicketsWithNumbersForDrawOn(username, 1, numbers, drawDate); + + public void PlayerBuysTicketsWithNumbersForDrawOn(string username, int tickets, int[] numbers, + DateTime drawDate) => _drawManager.PurchaseTicket(drawDate, _playerManager.GetPlayer(username).PlayerId, numbers, 10 * tickets); + + public IList PlayerListsOpenTickets(string username) + => _drawManager.GetOpenTickets(_playerManager.GetPlayer(username).PlayerId); + + public IList PlayerListsTicketsForDrawOn(string username, DateTime drawDate) + => _drawManager.GetTickets(drawDate, _playerManager.GetPlayer(username).PlayerId); + + public void NumbersAreDrawnOn(int[] numbers, DateTime drawDate) => _drawManager.SettleDraw(drawDate, numbers); + } +} diff --git a/OnlineLottery/Test/SettlementTest.cs b/OnlineLottery/Test/SettlementTest.cs new file mode 100644 index 0000000..e135daf --- /dev/null +++ b/OnlineLottery/Test/SettlementTest.cs @@ -0,0 +1,106 @@ +using System; +using System.Linq; +using fit; +using fitlibrary; + +namespace OnlineLottery.Test +{ + public class SettlementTest : DoFixture + { + private readonly IDrawManager _drawManager; + private readonly IPlayerManager _playerManager; + private readonly DateTime _drawDate; + + public SettlementTest() + { + _playerManager = new PlayerManager(); + _drawManager = new DrawManager(_playerManager); + _drawDate = DateTime.Now; + _drawManager.CreateDraw(_drawDate); + } + + public Fixture AccountsBeforeTheDraw() => new CreatePlayerFixture(_playerManager); + + public Fixture TicketsInTheDraw() => new TicketPurchaseFixture(_playerManager, _drawManager, _drawDate); + + public void DrawResultsAre(int[] numbers) => _drawManager.SettleDraw(_drawDate, numbers); + + public Fixture PrizeDistribution() => new PrizePoolDistribution(_drawManager, _drawDate); + + public Fixture AccountsAfterTheDraw() => new BalanceCheckFixture(_playerManager); + } + + internal class CreatePlayerFixture : SetUpFixture + { + private readonly IPlayerManager _playerManager; + + public CreatePlayerFixture(IPlayerManager pm) + { + _playerManager = pm; + } + + public void PlayerBalance(string player, decimal balance) + { + var playerId = _playerManager.RegisterPlayer(new PlayerRegistrationInfo + { + Username = player, + Name = player, + Password = "XXXXXX" + }); + _playerManager.AdjustBalance(playerId, balance); + } + } + + internal class TicketPurchaseFixture : SetUpFixture + { + private readonly IDrawManager _drawManager; + private readonly DateTime _drawDate; + private readonly IPlayerManager _playerManager; + + public TicketPurchaseFixture(IPlayerManager pm, IDrawManager dm, DateTime drawDate) + { + _drawManager = dm; + _drawDate = drawDate; + _playerManager = pm; + } + + public void PlayerNumbersValue(string player, int[] numbers, decimal value) + { + _drawManager.PurchaseTicket(_drawDate, _playerManager.GetPlayer(player).PlayerId, numbers, value); + } + } + + internal class PrizePoolDistribution : ColumnFixture + { + private readonly IDrawManager _drawManager; + private readonly DateTime _drawDate; + + public PrizePoolDistribution(IDrawManager dm, DateTime drawDate) + { + _drawManager = dm; + _drawDate = drawDate; + } + + public int WinningTickets() => _drawManager.GetDraw(_drawDate).Tickets.Count(ticket => ticket.Winnings != 0); + + public decimal[] PrizeMoney() + { + var prizes = _drawManager.GetDraw(_drawDate).Tickets.Where(ticket => ticket.Winnings > 0).Select(ticket => ticket.Winnings).ToArray(); + Array.Sort(prizes); + return prizes; + } + } + + internal class BalanceCheckFixture : ColumnFixture + { + private readonly IPlayerManager _playerManager; + + public BalanceCheckFixture(IPlayerManager pm) + { + _playerManager = pm; + } + + public string Player; + public decimal Balance => _playerManager.GetPlayer(Player).Balance; + } +} diff --git a/OnlineLottery/Ticket.cs b/OnlineLottery/Ticket.cs index 0a06626..b56b71e 100644 --- a/OnlineLottery/Ticket.cs +++ b/OnlineLottery/Ticket.cs @@ -10,11 +10,15 @@ public Ticket(IPlayerInfo player, DateTime drawDate, int[] numbers, decimal valu Holder = player; DrawDate = drawDate; Value = value; + IsOpen = true; + Winnings = 0; } public int[] Numbers { get; } public IPlayerInfo Holder { get; } public DateTime DrawDate { get; } public decimal Value { get; } + public bool IsOpen { get; set; } + public decimal Winnings { get; set; } } } \ No newline at end of file