public void min_bet_cannot_be_greater_max_bet() { Assert.Throws <ArgumentException>(() => { IBettingShop <string> _ = new DefaultBettingShop <string>( _ => Task.FromResult(100L), minBet: 101, maxBet: 100); }); }
public async Task cannot_lower_bet() { IBettingShop <string> bettingShop = new DefaultBettingShop <string>(_ => Task.FromResult(long.MaxValue)); Assert.Null(await bettingShop.PlaceBet("user", Side.Blue, 100)); PlaceBetFailure?failure = await bettingShop.PlaceBet("user", Side.Blue, 99); Assert.IsInstanceOf <PlaceBetFailure.CannotLowerBet>(failure); Assert.AreEqual(100, (failure as PlaceBetFailure.CannotLowerBet)?.ExistingBet); }
public void no_bets_is_no_error() { IBettingShop <string> bettingShop = new DefaultBettingShop <string>(_ => Task.FromResult(0L)); // not a useful case, but it should gracefully handle a possibly 0/0 IImmutableDictionary <Side, double> odds = bettingShop.GetOdds(); Assert.AreEqual(1.0d, odds[Side.Blue]); Assert.AreEqual(1.0d, odds[Side.Red]); }
public async Task cannot_change_side() { IBettingShop <string> bettingShop = new DefaultBettingShop <string>(_ => Task.FromResult(long.MaxValue)); Assert.Null(await bettingShop.PlaceBet("user", Side.Blue, 100)); PlaceBetFailure?failure = await bettingShop.PlaceBet("user", Side.Red, 200); Assert.IsInstanceOf <PlaceBetFailure.CannotChangeSide>(failure); Assert.AreEqual(Side.Blue, (failure as PlaceBetFailure.CannotChangeSide)?.SideBetOn); }
public async Task bets_on_only_one_side_bets_is_no_error() { IBettingShop <string> bettingShop = new DefaultBettingShop <string>(_ => Task.FromResult(long.MaxValue)); Assert.Null(await bettingShop.PlaceBet("user", Side.Blue, 1)); // not a useful case, but it should gracefully handle a possibly x/0 IImmutableDictionary <Side, double> odds = bettingShop.GetOdds(); Assert.AreEqual(0.0d, odds[Side.Blue]); Assert.AreEqual(double.PositiveInfinity, odds[Side.Red]); }
public async Task heavily_one_sided_bets_are_accurate() { IBettingShop <string> bettingShop = new DefaultBettingShop <string>(_ => Task.FromResult(long.MaxValue)); Assert.Null(await bettingShop.PlaceBet("userBlue", Side.Blue, 1)); Assert.Null(await bettingShop.PlaceBet("userRedSmall", Side.Red, 1)); Assert.Null(await bettingShop.PlaceBet("userRedBig", Side.Red, 999_999_999_999)); IImmutableDictionary <Side, double> odds = bettingShop.GetOdds(); Assert.AreEqual(1_000_000_000_000d, odds[Side.Blue]); Assert.AreEqual(0.000_000_000_001d, odds[Side.Red]); }
public async Task sum_money_won_equals_sum_money_lost() { IBettingShop <string> bettingShop = new DefaultBettingShop <string>(_ => Task.FromResult(long.MaxValue)); var(blue1, blue2, red1, red2) = (100, 150, 180, 220); Assert.Null(await bettingShop.PlaceBet("userBlue1", Side.Blue, blue1)); Assert.Null(await bettingShop.PlaceBet("userBlue2", Side.Blue, blue2)); Assert.Null(await bettingShop.PlaceBet("userRed1", Side.Red, red1)); Assert.Null(await bettingShop.PlaceBet("userRed2", Side.Red, red2)); IImmutableDictionary <Side, double> odds = bettingShop.GetOdds(); Assert.AreEqual(red1 + red2, (blue1 + blue2) * odds[Side.Blue]); // if blue won Assert.AreEqual(blue1 + blue2, (red1 + red2) * odds[Side.Red]); // if red won }
public async Task available_funds_self_referential() { IBettingShop <string> bettingShop = null !; // ReSharper disable once AccessToModifiedClosure bettingShop = new DefaultBettingShop <string>( user => Task.FromResult(100 - bettingShop.GetBetsForUser(user).Sum(kvp => kvp.Value))); PlaceBetFailure?failure1 = await bettingShop.PlaceBet("user", Side.Blue, 101); Assert.IsInstanceOf <PlaceBetFailure.InsufficientFunds>(failure1); Assert.AreEqual(100, (failure1 as PlaceBetFailure.InsufficientFunds)?.AvailableMoney); Assert.Null(await bettingShop.PlaceBet("user", Side.Blue, 99)); // already bet amount must be incorporated into available money, since the bet gets replaced Assert.Null(await bettingShop.PlaceBet("user", Side.Blue, 100)); PlaceBetFailure?failure2 = await bettingShop.PlaceBet("user", Side.Blue, 101); Assert.IsInstanceOf <PlaceBetFailure.InsufficientFunds>(failure2); Assert.AreEqual(100, (failure2 as PlaceBetFailure.InsufficientFunds)?.AvailableMoney); }
public async Task bet_too_low_or_too_high() { IBettingShop <string> bettingShop = new DefaultBettingShop <string>( _ => Task.FromResult(101L), minBet: 100, maxBet: 101); PlaceBetFailure?failureTooLow = await bettingShop.PlaceBet("user", Side.Blue, 99); Assert.IsInstanceOf <PlaceBetFailure.BetTooLow>(failureTooLow); Assert.AreEqual(100, (failureTooLow as PlaceBetFailure.BetTooLow)?.MinBet); PlaceBetFailure?failureTooHigh = await bettingShop.PlaceBet("user", Side.Blue, 102); Assert.IsInstanceOf <PlaceBetFailure.BetTooHigh>(failureTooHigh); Assert.AreEqual(101, (failureTooHigh as PlaceBetFailure.BetTooHigh)?.MaxBet); Assert.Null(await bettingShop.PlaceBet("user", Side.Blue, 100)); Assert.Null(await bettingShop.PlaceBet("user", Side.Blue, 101)); IImmutableDictionary <Side, IImmutableDictionary <string, long> > bets = bettingShop.GetBets(); Assert.AreEqual(1, bets[Side.Blue].Count); Assert.AreEqual(0, bets[Side.Red].Count); Assert.AreEqual(101, bets[Side.Blue]["user"]); }
private async Task Loop(CancellationToken cancellationToken) { var teams = new Teams { Blue = ImmutableList.Create(MatchTesting.TestVenonatForOverlay), Red = ImmutableList.Create(MatchTesting.TestVenonatForOverlay), }; await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken); await ResetBalances(); //ensure everyone has money to bet before the betting period const int matchId = -1; // TODO IBettingShop<User> bettingShop = new DefaultBettingShop<User>( async user => await _pokeyenBank.GetAvailableMoney(user)); bettingShop.BetPlaced += (_, args) => TaskToVoidSafely(_logger, () => _overlayConnection.Send(new MatchPokeyenBetUpdateEvent { MatchId = matchId, DefaultAction = "", NewBet = new Bet { Amount = args.Amount, Team = args.Side, BetBonus = 0 }, NewBetUser = args.User, Odds = bettingShop.GetOdds() }, cancellationToken)); _bettingPeriod = new BettingPeriod<User>(_pokeyenBank, bettingShop); _bettingPeriod.Start(); IMatchCycle match = new CoinflipMatchCycle(_loggerFactory.CreateLogger<CoinflipMatchCycle>()); Task setupTask = match.SetUp(new MatchInfo(teams.Blue, teams.Red), cancellationToken); await _overlayConnection.Send(new MatchCreatedEvent(), cancellationToken); await _overlayConnection.Send(new MatchBettingEvent(), cancellationToken); await _overlayConnection.Send(new MatchModesChosenEvent(), cancellationToken); // TODO await _overlayConnection.Send(new MatchSettingUpEvent { MatchId = 1234, Teams = teams, BettingDuration = _matchmodeConfig.DefaultBettingDuration.TotalSeconds, RevealDuration = 0, Gimmick = "speed", Switching = SwitchingPolicy.Never, BattleStyle = BattleStyle.Singles, InputOptions = new InputOptions { Moves = new MovesInputOptions { Policy = MoveSelectingPolicy.Always, Permitted = ImmutableList.Create("a", "b", "c", "d") }, Switches = new SwitchesInputOptions { Policy = SwitchingPolicy.Never, Permitted = ImmutableList<string>.Empty, RandomChance = 0 }, Targets = new TargetsInputOptions { Policy = TargetingPolicy.Disabled, Permitted = ImmutableList<string>.Empty, AllyHitChance = 0 }, }, BetBonus = 35, BetBonusType = "bet", }, cancellationToken); Duration bettingBeforeWarning = _matchmodeConfig.DefaultBettingDuration - _matchmodeConfig.WarningDuration; await Task.Delay(bettingBeforeWarning.ToTimeSpan(), cancellationToken); await _overlayConnection.Send(new MatchWarningEvent(), cancellationToken); await Task.Delay(_matchmodeConfig.WarningDuration.ToTimeSpan(), cancellationToken); await setupTask; _bettingPeriod.Close(); Task<MatchResult> performTask = match.Perform(cancellationToken); await _overlayConnection.Send(new MatchPerformingEvent { Teams = teams }, cancellationToken); MatchResult result = await performTask; await _overlayConnection.Send(new MatchOverEvent { MatchResult = result }, cancellationToken); // TODO log matches Dictionary<User, long> changes = await _bettingPeriod.Resolve(matchId, result, cancellationToken); await _overlayConnection.Send( new MatchResultsEvent { PokeyenResults = new PokeyenResults { Transactions = changes.ToImmutableDictionary(kvp => kvp.Key.Id, kvp => new Transaction { Change = kvp.Value, NewBalance = kvp.Key.Pokeyen }) } }, cancellationToken); await Task.Delay(_matchmodeConfig.ResultDuration.ToTimeSpan(), cancellationToken); await _overlayConnection.Send(new ResultsFinishedEvent(), cancellationToken); }