2
\$\begingroup\$

I am trying to setup an odds calculator for a best of 7 series, given independent odds for each event.

The following code works, but I would like to add recursion to simplify the end.

public class Game
{
    public int No { get; set; }
    public List<decimal> Odds;

    public Game(int no, decimal odd1, decimal odd2)
    {
        No = no;

        Odds = new List<decimal>();

        Odds.Add(odd1);
        Odds.Add(odd2);
    }

  }

void Main()
{

    var games = new List<Game>();

    var homeodd = .6m;
    var awayodd = .4m;

    var winningOdds = 0m;

//Add  7 games each with a different odd of winning game
    games.Add(new Game(1,homeodd,awayodd));
    games.Add(new Game(2,homeodd,awayodd));
    games.Add(new Game(3,awayodd,homeodd));
    games.Add(new Game(4,awayodd,homeodd));
    games.Add(new Game(5,awayodd,homeodd));
    games.Add(new Game(6,homeodd,awayodd));
    games.Add(new Game(7,homeodd,awayodd));

//game one has 2 possible outcomes, 0 = win, 1 = loss =>same for all 7 games
    for (int i = 0; i < 2; i++)
    {
        for (int j = 0; j < 2; j++)
        {
            for (int k = 0; k < 2; k++)
            {
                for (int l = 0; l < 2; l++)
                {
                    for (int m = 0; m < 2; m++)
                    {
                        for (int n = 0; n < 2; n++)
                        {
                            for (int o = 0; o < 2; o++)
                            {
                                if ((i+j+k+l+m+n+o)<4)  //if we have loss less than 4 games, we have won the series and we want to add the odds of that possibility to the total odds
                                {
                                    winningOdds += games[0].Odds[i] * games[1].Odds[j] * games[2].Odds[k] * games[3].Odds[l] * games[4].Odds[m] * games[5].Odds[n] * games[6].Odds[o];
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    Console.WriteLine(winningOdds); 
}
\$\endgroup\$
7
  • 2
    \$\begingroup\$ This is messy. You have Game class defined twice and some code directly in the class definition. Write it in a way, so that people don't have to think what you meant. \$\endgroup\$
    – Michal B.
    Oct 22, 2013 at 13:09
  • \$\begingroup\$ good catch, stupid cut and paste screwed me first time \$\endgroup\$ Oct 22, 2013 at 13:16
  • \$\begingroup\$ can you give some sample input and some expected results? there must be a better way to do this then to use all those for statements. what is inside the if statement will only run when all the variables are =0 . is that intended? \$\endgroup\$
    – Malachi
    Oct 22, 2013 at 13:37
  • \$\begingroup\$ for (int ii = 0; ii < Math.Pow(2, games.Count()); ii++) { var result = Convert.ToString(ii, 2); result = "00000000".Substring(0, 7-result.Length) + result; var flag = new List<int>(); for (int i2 = 0; i2 < games.Count (); i2++) { flag.Add(int.Parse(result.Substring(i2,1))); } if (flag.Sum() < 4) { winningOdds2 += games[0].Odds[flag[0]] * games[1].Odds[flag[1]] * games[2].Odds[flag[2]] * games[3].Odds[flag[3]] * games[4].Odds[flag[4]] * games[5].Odds[flag[5]] * games[6].Odds[flag[6]]; } } \$\endgroup\$ Oct 22, 2013 at 14:06
  • \$\begingroup\$ All the FOR loops can be replaced with this.... I just need to linq the multiplying line now \$\endgroup\$ Oct 22, 2013 at 14:07

2 Answers 2

4
\$\begingroup\$

Basically, what you need is a Cartesian product of 7 sequences, each containing a 0 and a 1. Eric Lippert has an interesting article about doing just that using LINQ (note: I think you could make his code more efficient using immutable collections). If you use his method, your code could look something like:

var product = CartesianProduct(Enumerable.Repeat(new[] { 0, 1 }, games.Count));

foreach (var indexes in product)
{
    if (indexes.Sum() < 4)
    {    
        winningOdds += indexes
            .Select((index, game) => games[game].Odds[index])
            .Aggregate((x, y) => x * y);
    }
}
\$\endgroup\$
0
\$\begingroup\$

This is a clean as I can make it. The last thing I could do would be to replace the hardcoded 7 by a const to make it a little easier to setup best of 5, best of 3, etc...

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication10
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var games = new List<Game>();

            const decimal homeodd = .6m;
            const decimal awayodd = .4m;

            var winningOdds2 = 0m;

            games.Add(new Game(1, homeodd));
            games.Add(new Game(2, homeodd));
            games.Add(new Game(3, awayodd));
            games.Add(new Game(4, awayodd));
            games.Add(new Game(5, awayodd));
            games.Add(new Game(6, homeodd));
            games.Add(new Game(7, homeodd));

            for (var ii = 0; ii < Math.Pow(2, games.Count()); ii++)
            {
                var result = Convert.ToString(ii, 2);
                result = "00000000".Substring(0, 7 - result.Length) + result;

                var flag = new List<int>();

                for (var i2 = 0; i2 < games.Count(); i2++)
                {
                    flag.Add(int.Parse(result.Substring(i2, 1)));
                }

                if (flag.Sum() >= 4) continue;
                var product = 1m;
                for (var p = 0; p < 7; p++)
                {
                    product = product * games[p].Odds[flag[p]];
                }
                winningOdds2 += product;
            }

            Console.WriteLine(winningOdds2);
            Console.ReadLine();
        }
    }
}

public class Game
{
    public int No { get; set; }

    public List<decimal> Odds;

    public Game(int no, decimal odd1)
    {
        No = no;
        Odds = new List<decimal> {odd1, 1-odd1};
    }
}

// Define other methods and classes here
\$\endgroup\$
3
  • 2
    \$\begingroup\$ 1. I would be very careful about using double as the loop variable. 2. If you want to add zero padding to a string, you can use PadLeft(), it's much more readable than your solution. 3. Instead of using digits in a string, I would rather access the bits directly using % and / or bitwise operators and shifts. \$\endgroup\$
    – svick
    Oct 22, 2013 at 14:38
  • \$\begingroup\$ I dont know how to directly access the bits, quick example please ? \$\endgroup\$ Oct 22, 2013 at 14:54
  • \$\begingroup\$ If you're okay with accessing the bits from least significant to most significant (basically, right to left), then it's something like while (i != 0) { int currentBit = i % 2; ...; i /= 2; }. \$\endgroup\$
    – svick
    Oct 22, 2013 at 15:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.