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"]);
        }
示例#10
0
        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);
        }