Blackjack game, 'Sequence contains no elements' - c#

I created a Blackjack game for coding school and I'm modifying it to display other aspects of the game, like what card value total the dealer had and what value total I had at the end of the game. It all works well until the Dealer Busted and I'm getting this error:
System.InvalidOperationException: 'Sequence contains no elements'
I've googled and searched Stackoverflow, but I didn't understand any of the responses. Here's my github code for it.
https://github.com/CrystalReimche/TwentyOneGame
In the TwentyOneGame.cs, line 143 is where where I'm trying to get it to display.
foreach (KeyValuePair<Player, int> entry in Bets)
{
Players.Where(x => x.Name == entry.Key.Name).First().Balance += (entry.Value * 2);
Console.WriteLine($"{entry.Key.Name} won {entry.Value} and now has a balance of {entry.Key.Balance}!");
Console.WriteLine($"Dealer had {TwentyOneRules.DealerCardValue(Dealer.Hand)} and {entry.Key.Name} had {TwentyOneRules.PlayerCardValue(entry.Key.Hand)}");
}
In the TwentyOneRules.cs holds the methods for it.
public static Dictionary<Face, int> _cardValues = new Dictionary<Face, int>()
{
[Face.Two] = 2,
[Face.Three] = 3,
[Face.Four] = 4,
[Face.Five] = 5,
[Face.Six] = 6,
[Face.Seven] = 7,
[Face.Eight] = 8,
[Face.Nine] = 9,
[Face.Ten] = 10,
[Face.Jack] = 10,
[Face.Queen] = 10,
[Face.King] = 10,
[Face.Ace] = 1
};
public static int[] GetAllPossibleHandValues(List<Card> Hand)
{
int aceCount = Hand.Count(x => x.Face == Face.Ace); // Find out how many Ace's there are
int[] result = new int[aceCount + 1]; // Plus 1 means if there's 2 Aces, there's 3 possible results ((1,1)||(1,11)||(11,11))
int value = Hand.Sum(x => _cardValues[x.Face]); // Value is the lowest possible value with all Ace's == 1
result[0] = value;
if (result.Length == 1) return result;
for (int i = 1; i < result.Length; i++)
{
value += (i * 10);
result[i] = value;
}
return result;
}
public static bool CheckForBlackJack(List<Card> Hand)
{
int[] possibleValues = GetAllPossibleHandValues(Hand);
int value = possibleValues.Max();
if (value == 2) return true;
else return false;
}
public static bool IsBusted(List<Card> Hand)
{
int value = GetAllPossibleHandValues(Hand).Min();
if (value > 21) return true;
else return false;
}
public static bool ShouldDealerStay(List<Card> Hand)
{
int[] possibleHandvalues = GetAllPossibleHandValues(Hand);
foreach (int value in possibleHandvalues)
{
if (value > 16 && value < 22)
{
return true;
}
}
return false;
}
public static bool? CompareHands(List<Card> PlayerHand, List<Card> DealerHand)
{
int[] playerResults = GetAllPossibleHandValues(PlayerHand);
int[] dealerResults = GetAllPossibleHandValues(DealerHand);
int playerScore = playerResults.Where(x => x < 22).Max(); // Filter the values that's < 22 and bring me the max of the values
int dealerScore = dealerResults.Where(x => x < 22).Max();
if (playerScore > dealerScore) return true;
else if (playerScore < dealerScore) return false;
else return null; // this is a tie
}
public static int PlayerCardValue(List<Card> PlayerHand)
{
int[] playerResults = GetAllPossibleHandValues(PlayerHand);
int playerScore = playerResults.Where(x => x < 50).Max();
return playerScore;
}
public static int DealerCardValue(List<Card> DealerHand)
{
int[] dealerResults = GetAllPossibleHandValues(DealerHand);
int dealerScore = dealerResults.Where(x => x < 22).Max();
return dealerScore;
}
I'm thinking it has something to do with the Dictionary, since the methods work on other parts of the game that do not use the Dictionary. For example, if I busted, it would display my card value total and the dealer card value total.
if (busted)
{
Console.WriteLine($"{player.Name} Busted! You lose your bet of {Bets[player]}. Your balance is now {player.Balance}");
Console.WriteLine($"Dealer had {TwentyOneRules.DealerCardValue(Dealer.Hand)} and {player.Name} had {TwentyOneRules.PlayerCardValue(player.Hand)}");
}
I just don't have a good grasp on how to use the Dictionary properly yet.

The problem is that you're invoking Max() (in either DealerCardValue or PlayerCardValue) on a sequence which contains no elements, as the Where() return value is an empty sequence, therefore there's no max value. This is written in the Max() function's documentation in MSDN.
Add DefaultIfEmpty() before Max() in DealerCardValue and PlayerCardValue to prevent InvalidOperationException from being thrown, as shown here, like so:
public static int DealerCardValue(List<Card> DealerHand)
{
int[] dealerResults = GetAllPossibleHandValues(DealerHand);
int dealerScore = dealerResults.Where(x => x < 22).DefaultIfEmpty().Max();
return dealerScore;
}

Related

Sorting an array of enumeration

Hello I have an array of enumerations, and I'm trying sort that array by their enumeration value and get the array index's of the top
private enum Values
{
LOW,
MEDIUM,
HIGH
}
private Values[] Settings = new Values[10];
Settings[0] = LOW;
Settings[1] = HIGH;
Settings[2] = MEDIUM;
Settings[3] = LOW;
Settings[4] = LOW;
Settings[5] = LOW;
Settings[6] = LOW;
Settings[7] = LOW;
Settings[8] = MEDIUM;
Settings[9] = MEDIUM;
Basically now, with what I have above, I need to sort the settings array by enumeration value and get the array indexes of the top (let's say 3) items;
So I'd get back values 1, 2, 8
The platform I'm using does not support LINQ so those handy functions are not available.
I've been trying to wrap my head around this but it would help to have another pair of eyes.
thanks.
Implement a wrapper reference type,
class ValueWrapper : IComparable<ValueWrapper>
{
public Values Value { get; set; }
public int Index { get; set; }
public int CompareTo(ValueWrapper other)
{
return this.Value.CompareTo(other.Value) * -1; // Negating since you want reversed order
}
}
Usage -
ValueWrapper[] WrappedSettings = new ValueWrapper[10];
for(int i = 0; i < WrappedSettings.Length; i++)
{
WrappedSettings[i] = new ValueWrapper { Value = Settings[i], Index = i };
}
Array.Sort(WrappedSettings);
WrappedSettings will be sorted as you specified, preserving the indexes they were in the original array.
how about this:
Values first = Values.Low,second = Values.Low,third = Values.Low;
int firstI = -1,secondI = -1, thirdI = -1;
for(int i = 0;i < Settings.Length;i++)
{
if(Settings[i] > first || firstI == -1)
{
third = second;
thirdI = secondI;
second= first;
secondI= firstI;
first = Settings[i];
firstI = i;
}
else if(Settings[i] > second || secondI == -1)
{
third = second;
thirdI = secondI;
second = Settings[i];
secondI = i;
}
else if(Settings[i] > third || thirdI == -1)
{
third = Settings[i];
thirdI = i;
}
}
So, since you say you work in an environment where Linq is not available, I assume that other things like generics, nullables and so on will not be available either. A very low-tech solution.
Basic idea:
For every possible enum value, from highest to lowest, go through the list. If we find that value, output it and remember how many we have output. If we reach 3, stop the algorithm.
So, we first look for HIGH in the list, then for MEDIUM and so on.
class Program
{
private enum Values
{
LOW,
MEDIUM,
HIGH
}
static void Main(string[] args)
{
// Sample data
Values[] settings = new Values[10];
settings[0] = Values.LOW;
settings[1] = Values.HIGH;
settings[2] = Values.MEDIUM;
settings[3] = Values.LOW;
settings[4] = Values.LOW;
settings[5] = Values.LOW;
settings[6] = Values.LOW;
settings[7] = Values.LOW;
settings[8] = Values.MEDIUM;
settings[9] = Values.MEDIUM;
// Get Values of the enum type
// This list is sorted ascending by value but may contain duplicates
Array enumValues = Enum.GetValues(typeof(Values));
// Number of results found so far
int numberFound = 0;
// The enum value we used during the last outer loop, so
// we skip duplicate enum values
int lastValue = -1;
// For each enum value starting with the highest to the lowest
for (int i= enumValues.Length -1; i >= 0; i--)
{
// Get this enum value
int enumValue = (int)enumValues.GetValue(i);
// Check whether we had the same value in the previous loop
// If yes, skip it.
if(enumValue == lastValue)
{
continue;
}
lastValue = enumValue;
// For each entry in the list where we are searching
for(int j=0; j< settings.Length; j++)
{
// Check to see whether it is the currently searched value
if (enumValue == (int)settings[j])
{
// if yes, then output it.
Console.WriteLine(j);
numberFound++;
// Stop after 3 found entries
if (numberFound == 3)
{
goto finished;
}
}
}
}
finished:
Console.ReadLine();
}
}
Output is as requested 1,2,8
I'm not sure if this is exactly what you want because it doesn't sort the original array, but one way to get the indexes of the top three values is to simply store the indexes of the top values in another array. Then we can loop through the original array, and for each item, see if it's larger than any of the items at the indexes we've stored so far. If it is, then swap it with that item.
For example:
// Start the topIndexes array with all invalid indexes
var topIndexes = new[] {-1, -1, -1};
for (var settingIndex = 0; settingIndex < Settings.Length; settingIndex++)
{
var setting = Settings[settingIndex];
var topIndexLessThanSetting = -1;
// Find the smallest topIndex setting that's less than this setting
for (int topIndex = 0; topIndex < topIndexes.Length; topIndex++)
{
if (topIndexes[topIndex] == -1)
{
topIndexLessThanSetting = topIndex;
break;
}
if (setting <= Settings[topIndexes[topIndex]]) continue;
if (topIndexLessThanSetting == -1 ||
Settings[topIndexes[topIndex]] < Settings[topIndexes[topIndexLessThanSetting]])
{
topIndexLessThanSetting = topIndex;
}
}
topIndexes[topIndexLessThanSetting] = settingIndex;
}
// topIndexes = { 1, 2, 8 }

How to distribute items evenly, without random numbers

I have a situation where I need to evenly distribute N items across M slots. Each item has its own distribution %. For discussion purposes say there are three items (a,b,c) with respective percentages of (50,25,25) to be distributed evenly across 20 slots. Hence 10 X a,5 X b & 5 X c need to be distributed. The outcome would be as follows:
1. a
2. a
3. c
4. b
5. a
6. a
7. c
8. b
9. a
10. a
11. c
12. b
13. a
14. a
15. c
16. b
17. a
18. a
19. c
20. b
The part that I am struggling with is that the number of slots, number of items and percentages can all vary, of course the percentage would always total up to 100%. The code that I wrote resulted in following output, which is always back weighted in favour of item with highest percentage. Any ideas would be great.
1. a
2. b
3. c
4. a
5. b
6. c
7. a
8. b
9. c
10. a
11. c
12. b
13. a
14. b
15. c
16. a
17. a
18. a
19. a
20. a
Edit
This is what my code currently looks like. Results in back weighted distribution as I mentioned earlier. For a little context, I am trying to evenly assign commercials across programs. Hence every run with same inputs has to result in exactly the same output. This is what rules out the use of random numbers.
foreach (ListRecord spl in lstRecords){
string key = spl.AdvertiserName + spl.ContractNumber + spl.AgencyAssignmentCode;
if (!dictCodesheets.ContainsKey(key)){
int maxAssignmentForCurrentContract = weeklyList.Count(c => (c.AdvertiserName == spl.AdvertiserName) && (c.AgencyAssignmentCode == spl.AgencyAssignmentCode)
&& (c.ContractNumber == spl.ContractNumber) && (c.WeekOf == spl.WeekOf));
int tmpAssignmentCount = 0;
for (int i = 0; i < tmpLstGridData.Count; i++)
{
GridData gData = tmpLstGridData[i];
RotationCalculation commIDRotationCalc = new RotationCalculation();
commIDRotationCalc.commercialID = gData.commercialID;
commIDRotationCalc.maxAllowed = (int)Math.Round(((double)(maxAssignmentForCurrentContract * gData.rotationPercentage) / 100), MidpointRounding.AwayFromZero);
tmpAssignmentCount += commIDRotationCalc.maxAllowed;
if (tmpAssignmentCount > maxAssignmentForCurrentContract)
{
commIDRotationCalc.maxAllowed -= 1;
}
if (i == 0)
{
commIDRotationCalc.maxAllowed -= 1;
gridData = gData;
}
commIDRotationCalc.frequency = (int)Math.Round((double)(100/gData.rotationPercentage));
if (i == 1)
{
commIDRotationCalc.isNextToBeAssigned = true;
}
lstCommIDRotCalc.Add(commIDRotationCalc);
}
dictCodesheets.Add(key, lstCommIDRotCalc);
}else{
List<RotationCalculation> lstRotCalc = dictCodesheets[key];
for (int i = 0; i < lstRotCalc.Count; i++)
{
if (lstRotCalc[i].isNextToBeAssigned)
{
gridData = tmpLstGridData.Where(c => c.commercialID == lstRotCalc[i].commercialID).FirstOrDefault();
lstRotCalc[i].maxAllowed -= 1;
if (lstRotCalc.Count != 1)
{
if (i == lstRotCalc.Count - 1 && lstRotCalc[0].maxAllowed > 0)
{
//Debug.Print("In IF");
lstRotCalc[0].isNextToBeAssigned = true;
lstRotCalc[i].isNextToBeAssigned = false;
if (lstRotCalc[i].maxAllowed == 0)
{
lstRotCalc.RemoveAt(i);
}
break;
}
else
{
if (lstRotCalc[i + 1].maxAllowed > 0)
{
//Debug.Print("In ELSE");
lstRotCalc[i + 1].isNextToBeAssigned = true;
lstRotCalc[i].isNextToBeAssigned = false;
if (lstRotCalc[i].maxAllowed == 0)
{
lstRotCalc.RemoveAt(i);
}
break;
}
}
}
}
}
}
}
Edit 2
Trying to clear up my requirement here. Currently, because item 'a' is to be assigned 10 times which is the highest among all three items, towards the end of distribution, items 16 - 20 all have been assigned only 'a'. As has been asked in comments, I am trying to achieve a distribution that "looks" more even.
One way to look at this problem is as a multi-dimensional line drawing problem. So I used Bresenham's line algorithm to create the distribution:
public static IEnumerable<T> GetDistribution<T>( IEnumerable<Tuple<T, int>> itemCounts )
{
var groupCounts = itemCounts.GroupBy( pair => pair.Item1 )
.Select( g => new { Item = g.Key, Count = g.Sum( pair => pair.Item2 ) } )
.OrderByDescending( g => g.Count )
.ToList();
int maxCount = groupCounts[0].Count;
var errorValues = new int[groupCounts.Count];
for( int i = 1; i < errorValues.Length; ++i )
{
var item = groupCounts[i];
errorValues[i] = 2 * groupCounts[i].Count - maxCount;
}
for( int i = 0; i < maxCount; ++i )
{
yield return groupCounts[0].Item;
for( int j = 1; j < errorValues.Length; ++j )
{
if( errorValues[j] > 0 )
{
yield return groupCounts[j].Item;
errorValues[j] -= 2 * maxCount;
}
errorValues[j] += 2 * groupCounts[j].Count;
}
}
}
The input is the actual number of each item you want. This has a couple advantages. First it can use integer arithmetic, which avoids any rounding issues. Also it gets rid of any ambiguity if you ask for 10 items and want 3 items evenly distributed (which is basically just the rounding issue again).
Here's one with no random number that gives the required output.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// name, percentage
Dictionary<string, double> distribution = new Dictionary<string,double>();
// name, amount if one more were to be distributed
Dictionary<string, int> dishedOut = new Dictionary<string, int>();
//Initialize
int numToGive = 20;
distribution.Add("a", 0.50);
distribution.Add("b", 0.25);
distribution.Add("c", 0.25);
foreach (string name in distribution.Keys)
dishedOut.Add(name, 1);
for (int i = 0; i < numToGive; i++)
{
//find the type with the lowest weighted distribution
string nextUp = null;
double lowestRatio = double.MaxValue;
foreach (string name in distribution.Keys)
if (dishedOut[name] / distribution[name] < lowestRatio)
{
lowestRatio = dishedOut[name] / distribution[name];
nextUp = name;
}
//distribute it
dishedOut[nextUp] += 1;
Console.WriteLine(nextUp);
}
Console.ReadLine();
}
}
Instead of a truly random number generator, use a fixed seed, so that the program has the same output every time you run it (for the same input). In the code below, the '0' is the seed, which means the 'random' numbers generated will always be the same each time the program is run.
Random r = new Random(0);
//AABC AABC…
int totalA = 10
int totalB = 5
int totalC = 5
int totalItems = 20 //A+B+C
double frequencyA = totalA / totalItems; //0.5
double frequencyB = totalB / totalItems; //0.25
double frequencyC = totalC / totalItems; //0.25
double filledA = frequencyA;
double filledB = frequencyB;
double filledC = frequencyC;
string output = String.Empty;
while(output.Length < totalItems)
{
filledA += frequencyA;
filledB += frequencyB;
filledC += frequencyC;
if(filledA >= 1)
{
filledA -= 1;
output += "A";
if(output.Length == totalItems){break;}
}
if(filledB >= 1)
{
filledB -= 1
output += "B";
if(output.Length == totalItems){break;}
}
if(filledC >= 1)
{
filledC -= 1
output += "C";
if(output.Length == totalItems){break;}
}
}
This answer was mostly stolen and lightly adapted for your use from here
My idea is that you distribute your items in the simplest way possible without care of order, then shuffle the list.
public static void ShuffleTheSameWay<T>(this IList<T> list)
{
Random rng = new Random(0);
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Fiddle here

Get highest number from each row of a triangle and sum it up

I have below triangle of numbers which will be sent as parameter to a function
5
9 6
4 6 8
0 7 1 5
Now this will be received as string in below function with the format 5#9#6#4#6#8#0#7#1#5. So far I've tried to ripple only the digits from #
public class Sample
{
public static string validtrianglesum(string input)
{
string sum="0";
foreach(char num in input)
{
if(!num.Equals('#'))
{
Console.PrintLine(num); //here am getting only the nums excluding #
//How to sum up based on each row
}
}
return sum; //return
}
}
how could highest number from each row and sum them and how could I identify the rows to sum it up? Hope to find some help.
Let's break this down as follows:
Firstly, turn the input into an array of numbers:
string input = "5#9#6#4#6#8#0#7#1#5";
var numbers = input.Split('#').Select(int.Parse).ToArray();
Now let's assume we have a MakeTriangular(int[]) method that turns an array of numbers into a sequence of rows with the first row being of length 1, the second of length 2 and so on, so that it returns IEnumerable<IEnumerable<int>>.
Then we can use that along with Linq to calculate the sum of the maximum value in each row as follows:
int sum = MakeTriangular(numbers).Sum(row => row.Max());
Which gives the answer.
The implementation of MakeTriangular() could look like this:
public static IEnumerable<IEnumerable<int>> MakeTriangular(int[] numbers)
{
for (int i = 0, len = 1; i < numbers.Length; i += len, ++len)
yield return new ArraySegment<int>(numbers, i, len);
}
Putting it all together into a compilable Console app:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
string input = "5#9#6#4#6#8#0#7#1#5";
var numbers = input.Split('#').Select(int.Parse).ToArray();
int sum = MakeTriangular(numbers).Sum(row => row.Max());
Console.WriteLine(sum);
}
public static IEnumerable<IEnumerable<int>> MakeTriangular(int[] numbers)
{
for (int i = 0, len = 1; i < numbers.Length; i += len, ++len)
yield return new ArraySegment<int>(numbers, i, len);
}
}
}
Summing up all values in each row:
private static IEnumerable<int> Sum(string input)
{
int i = 0, s = 0, z = 1;
foreach (var v in input.Split('#').Select(int.Parse))
{
s += v;
if (++i != z) continue;
z++;
yield return s;
s = i = 0;
}
}
The same in one line:
private static IEnumerable<int> Sum(string input) => new Func<int, int, IEnumerable<int>>((i, z) => input.Split('#').Select(int.Parse).GroupBy(e => i++ == z && (i = 1) != null ? ++z : z, e => e).Select(e => e.Sum()))(0, 1);
Summing up all the highest values in each row:
private static int Sum(string input)
{
int i = 0, s = 0, z = 1, m = 0;
foreach (var v in input.Split('#').Select(int.Parse))
{
if (v > m) m = v;
if (++i != z) continue;
z++;
s += m;
i = m = 0;
}
return s;
}
Same in one line:
private static int Sum(string input) => new Func<int, int, int>((i, z) => input.Split('#').Select(int.Parse).GroupBy(e => i++ == z && (i = 1) != null ? ++z : z, e => e).Select(e => e.Max()).Sum())(0, 1);
I am returning the sums as IEnumerable<int> and with the yield return. If you just want to print out the answers change the return type to void and remove the yield return s; line.
One way to solve this is to determine the size of the triangle. By size I mean the height/width. E.g, the provided triangle has a size of 4.
If the size is n then the number of elements in the triangle will be n(n + 1)/2. When the number of elements in the input is known this can be solved to determine n (the size) by solving a second degree polynomial and picking the positive solution (the expression below involving a square root):
var triangle = "5#9#6#4#6#8#0#7#1#5";
var values = triangle.Split('#').Select(Int32.Parse).ToList();
var sizeAsDouble = (-1 + Math.Sqrt(1 + 8*values.Count))/2;
var size = (Int32) sizeAsDouble;
if (sizeAsDouble != size)
throw new ArgumentException("Input data is not a triangle.");
So with the provided input size will be 4. You can then use the size to select each row in the triangle and perform the desired arithmetic:
var maxValues = Enumerable
.Range(0, size)
.Select(i => new { Start = i*(i + 1)/2, Count = i + 1 })
.Select(x => values.Skip(x.Start).Take(x.Count))
.Select(v => v.Max());
The first Select will compute the necessary indices to correctly slice the array of values which is done in the second Select. Again the formula n(n + 1)/2 is used. If you want to you can merge some of these Select operations but I think spliting them up makes it clearer what is going on.
The output of this will be the numbers 5, 9, 8, 7. If you want to sum these you can do it like this:
return maxValues.Sum();
You can use LINQ:
string input = "5#9#6#4#6#8#0#7#1#5";
var nums = input.Split('#').Select(s => Int32.Parse(s));
var res = Enumerable.Range(0, nums.Count())
.Select(n => nums.Skip(Enumerable.Range(0, n).Sum()).Take(n));
.Where(x => x.Any()); // here you have IEnumerable<int> for every row
.Select(arr => arr.Max());
Please give credit to Widi :) but this is your request
var rows = Sum("5#9#6#4#6#8#0#7#1#5");
var total = rows.Sum();
private static IEnumerable<int> Sum(string inp)
{
int i = 0, s = 0, z = 1;
foreach (var v in inp.Split('#').Select(int.Parse))
{
s = Math.Max(s, v);
if (++i == z)
{
z++;
yield return s;
s = i = 0;
}
}
}
I would use 2 functions:
1st one to convert the string into a tree representation:
List<List<int>> GetTree(string data)
{
List<List<int>> CompleteTree = new List<List<int>>();
List<int> ValuesInLine = new List<int>();
int partsinrow = 1;
int counter = 0;
foreach (string part in data.Split('#'))
{
int value = int.Parse(part);
ValuesInLine.Add(value);
if (++counter == partsinrow)
{
CompleteTree.Add(ValuesInLine);
ValuesInLine = new List<int>();
counter = 0;
partsinrow++;
}
}
return CompleteTree;
}
2nd one to sum up the maximum of the lines:
int GetSumOfTree(List<List<int>> tree)
{
int sum = 0;
foreach (List<int> line in tree)
{
line.Sort();
int max = line[line.Count - 1];
sum += max;
}
return sum;
}

Looking up values based on range data

What data structure or datatype would be good for holding data ranges, and return a value based on data that lies in that range?
For example suppose I have the following ranges
1-10 -> 1
11-35 -> 2.5
36-49-> 3.8
50-60 -> 1.2
61-80 -> 0.9
In this case given the number 41 I would want the number 3.8 returned (as 41 is between the ranges of 36 and 49).
Is there a clever way of representing such data in a datastructure in order to perform this lookup?
A comparatively convenient and very performant implementation would be to use a SortedList<int, Tuple<int, double>>. Use the lower bound for each segment as the key and a tuple of upper bound + mapped value for the value:
var list = new SortedList<int, Tuple<int, double>>
{
{ 1, Tuple.Create(10, 1.0) },
{ 11, Tuple.Create(35, 2.5) },
};
(Of course you could decide to use a better-looking data structure to declare your parameters in order to enhance code maintainability and internally convert to this before getting down to business).
Since list.Keys is guaranteed to be sorted, when looking up a value you can use binary search on it to find the index that is equal to or greater than your input value:
var index = list.Keys.BinarySearch(value);
if (index < 0 && ~index == 0) {
// no match, stop processing
}
else if (index < 0) {
// key not found as is, look at the previous interval
index = ~index - 1;
}
At this point index points at the only range that might include value, so all that remains is to test for that:
if(x >= list.Keys[index] && x <= list.Values[index].Item1) {
var result = list.Values[index].Item2;
}
else {
// no match
}
You wouldn't call this "clean", but it's very short and very fast.
You can use this code
Key :
public class Interval<T> where T : IComparable
{
public Nullable<T> Start { get; set; }
public Nullable<T> End { get; set; }
public Interval(T start, T end)
{
Start = start;
End = end;
}
public bool InRange(T value)
{
return ((!Start.HasValue || value.CompareTo(Start.Value) > 0) &&
(!End.HasValue || End.Value.CompareTo(value) > 0));
}
}
value : decimal
And you can use this Type : Dictionary<Interval, decimal>
Nota : you can define access methods
It took me a while but I have a QuickAndDirty method which assumes all given values are valid and ranges are adjacent, without using any data structures.
And a very specific data structure which will only return something if the given index is exactly in the specified range and which can be expanded at run-time.
public abstract class TreeNode
{
public static double QuickAndDirty(int index)
{
double result = 1.0;
if (index > 10)
result = 2.5;
if (index > 35)
result = 3.8;
if (index > 49)
result = 1.2;
if (index > 60)
result = 0.9;
return result;
}
public abstract double GetValue(int index);
public abstract TreeNode AddRange(int begin, int end, double value);
public static TreeNode MakeTreePart(Tuple<int, int, double>[] ranges)
{
return TreeNode.MakeTreePart(ranges, 0, ranges.Length >> 1, ranges.Length - 1);
}
private static TreeNode MakeTreePart(Tuple<int, int, double>[] ranges, int min, int index, int max)
{
if (index == min || index == max)
return new Leaf(ranges[index].Item1, ranges[index].Item2, ranges[index].Item3);
return new SegmentTree(
ranges[index].Item2 + .5,
TreeNode.MakeTreePart(ranges, min, index >> 1, index - 1),
TreeNode.MakeTreePart(ranges, index + 1, index << 1, max));
}
}
public class SegmentTree : TreeNode
{
private double pivot;
private TreeNode left, right;
public SegmentTree(double pivot, TreeNode left, TreeNode right)
{
this.pivot = pivot;
this.left = left;
this.right = right;
}
public override double GetValue(int index)
{
if (index < pivot)
return left.GetValue(index);
return right.GetValue(index);
}
public override TreeNode AddRange(int begin, int end, double value)
{
if (end < pivot)
this.left = this.left.AddRange(begin, end, value);
else
this.right = this.right.AddRange(begin, end, value);
// Do this to confirm to the interface.
return this;
}
}
public class Leaf : TreeNode
{
private int begin, end;
private double value;
public Leaf(int begin, int end, double value)
{
this.begin = begin;
this.end = end;
this.value = value;
}
public override double GetValue(int index)
{
if (index >= begin && index <= end)
return value;
throw new Exception("index out of range");
}
public override TreeNode AddRange(int begin, int end, double value)
{
if (this.end < begin)
return new SegmentTree(((double)this.end + begin) * .5, this, new Leaf(begin, end, value));
else if (end < this.begin)
return new SegmentTree(((double)end + this.begin) * .5, new Leaf(begin, end, value), this);
else throw new Exception("Indexes overlap.");
}
}
static void Main()
{
TreeNode start = new Leaf(36, 49, 3.8);
start = start.AddRange(11, 35, 2.5);
start = start.AddRange(1, 10, 1.0);
start = start.AddRange(50, 60, 1.2);
start = start.AddRange(61, 80, 0.9);
double[] values = new double[70];
for (int i = 1; i < values.Length; i++)
values[i] = start.GetValue(i);
TreeNode array = TreeNode.MakeTreePart(
new Tuple<int, int, double>[]
{
Tuple.Create(1, 10, 1.0),
Tuple.Create(11, 35, 2.5),
Tuple.Create(36, 49, 3.8),
Tuple.Create(50, 60, 1.2),
Tuple.Create(61, 80, 0.9)
});
for (int i = 1; i < values.Length; i++)
values[i] = start.GetValue(i);
}

C#: Dice Permutation without Repetition

How can I change my C# code below to list all possible permutations without repetitions? For example: The result of 2 dice rolls would produce 1,1,2 so that means 2,1,1 should not appear.
Below is my code:
string[] Permutate(int input)
{
string[] dice;
int numberOfDice = input;
const int diceFace = 6;
dice = new string[(int)Math.Pow(diceFace, numberOfDice)];
int indexNumber = (int)Math.Pow(diceFace, numberOfDice);
int range = (int)Math.Pow(diceFace, numberOfDice) / 6;
int diceNumber = 1;
int counter = 0;
for (int i = 1; i <= indexNumber; i++)
{
if (range != 0)
{
dice[i - 1] += diceNumber + " ";
counter++;
if (counter == range)
{
counter = 0;
diceNumber++;
}
if (i == indexNumber)
{
range /= 6;
i = 0;
}
if (diceNumber == 7)
{
diceNumber = 1;
}
}
Thread.Sleep(1);
}
return dice;
}
The simplest possible way I could think of:
List<string> dices = new List<string>();
for (int i = 1; i <= 6; i++)
{
for (int j = i; j <= 6; j++)
{
for (int k = j; k <= 6; k++)
{
dices.Add(string.Format("{0} {1} {2}", i, j, k));
}
}
}
I have written a class to handle common functions for working with the binomial coefficient, which is the type of problem that your problem falls under. It performs the following tasks:
Outputs all the K-indexes in a nice format for any N choose K to a file. The K-indexes can be substituted with more descriptive strings or letters. This method makes solving this type of problem quite trivial.
Converts the K-indexes to the proper index of an entry in the sorted binomial coefficient table. This technique is much faster than older published techniques that rely on iteration. It does this by using a mathematical property inherent in Pascal's Triangle. My paper talks about this. I believe I am the first to discover and publish this technique, but I could be wrong.
Converts the index in a sorted binomial coefficient table to the corresponding K-indexes.
Uses Mark Dominus method to calculate the binomial coefficient, which is much less likely to overflow and works with larger numbers.
The class is written in .NET C# and provides a way to manage the objects related to the problem (if any) by using a generic list. The constructor of this class takes a bool value called InitTable that when true will create a generic list to hold the objects to be managed. If this value is false, then it will not create the table. The table does not need to be created in order to perform the 4 above methods. Accessor methods are provided to access the table.
There is an associated test class which shows how to use the class and its methods. It has been extensively tested with 2 cases and there are no known bugs.
To read about this class and download the code, see Tablizing The Binomial Coeffieicent.
I'm bad at math as well, this may or may not be helpful...
Program.cs
namespace Permutation
{
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Generating list.");
var dice = new List<ThreeDice>();
for (int x = 1; x <= 6; x++)
{
for (int y = 1; y <= 6; y++)
{
for (int z = 1; z <= 6; z++)
{
var die = new ThreeDice(x, y, z);
if (dice.Contains(die))
{
Console.WriteLine(die + " already exists.");
}
else
{
dice.Add(die);
}
}
}
}
Console.WriteLine(dice.Count + " permutations generated.");
foreach (var die in dice)
{
Console.WriteLine(die);
}
Console.ReadKey();
}
}
}
ThreeDice.cs
namespace Permutation
{
using System;
using System.Collections.Generic;
public class ThreeDice : IEquatable<ThreeDice>
{
public ThreeDice(int dice1, int dice2, int dice3)
{
this.Dice = new int[3];
this.Dice[0] = dice1;
this.Dice[1] = dice2;
this.Dice[2] = dice3;
}
public int[] Dice { get; private set; }
// IEquatable implements this method. List.Contains() will use this method to see if there's a match.
public bool Equals(ThreeDice other)
{
// Get the current dice values into a list.
var currentDice = new List<int>(this.Dice);
// Check to see if the same values exist by removing them one by one.
foreach (int die in other.Dice)
{
currentDice.Remove(die);
}
// If the list is empty, we have a match.
return currentDice.Count == 0;
}
public override string ToString()
{
return "<" + this.Dice[0] + "," + this.Dice[1] + "," + this.Dice[2] + ">";
}
}
}
Good luck.
The important part of the question is that you want distinct sets (regardless of order). So for example, a dice roll of [1, 2, 1] is equal to a dice roll of [1, 1, 2].
I'm sure there are a number of ways to skin this cat, but the first thought that comes to mind is to create a EqualityComparer which will compare the list of dice in the way you want, and then use LINQ with the Distinct() method.
Here is the EqualityComparer, which takes 2 List<int> and says they are equal if the elements are all equal (regardless of order):
private class ListComparer : EqualityComparer<List<int>>
{
public override bool Equals(List<int> x, List<int> y)
{
if (x.Count != y.Count)
return false;
x.Sort();
y.Sort();
for (int i = 0; i < x.Count; i++)
{
if (x[i] != y[i])
return false;
}
return true;
}
public override int GetHashCode(List<int> list)
{
int hc = 0;
foreach (var i in list)
hc ^= i;
return hc;
}
}
And here is the code that uses it. I'm using LINQ to build up the list of all combinations... you could also do this with nested for loops but I like this better for some reason:
public static void Main()
{
var values = new[] { 1,2,3,4,5,6 };
var allCombos = from x in values
from y in values
from z in values
select new List<int>{ x, y, z };
var distinctCombos = allCombos.Distinct(new ListComparer());
Console.WriteLine("#All combos: {0}", allCombos.Count());
Console.WriteLine("#Distinct combos: {0}", distinctCombos.Count());
foreach (var combo in distinctCombos)
Console.WriteLine("{0},{1},{2}", combo[0], combo[1], combo[2]);
}
Hope that helps!
Here is generic c# version using recursion (basically the recursive method takes number of dices or number of times the dice has been tossed) and returns all the combinations strings ( for ex, for '3' as per the question - there will be 56 such combinations).
public string[] GetDiceCombinations(int noOfDicesOrnoOfTossesOfDice)
{
noOfDicesOrnoOfTossesOfDice.Throw("noOfDicesOrnoOfTossesOfDice",
n => n <= 0);
List<string> values = new List<string>();
this.GetDiceCombinations_Recursive(noOfDicesOrnoOfTossesOfDice, 1, "",
values);
return values.ToArray();
}
private void GetDiceCombinations_Recursive(int size, int index, string currentValue,
List<string> values)
{
if (currentValue.Length == size)
{
values.Add(currentValue);
return;
}
for (int i = index; i <= 6; i++)
{
this.GetDiceCombinations_Recursive(size, i, currentValue + i, values);
}
}
Below are corresponding tests...
[TestMethod]
public void Dice_Tests()
{
int[] cOut = new int[] { 6, 21, 56, 126 };
for(int i = 1; i<=4; i++)
{
var c = this.GetDiceCombinations(i);
Assert.AreEqual(cOut[i - 1], c.Length);
}
}

Categories