Cards Game - Subset Sum - Can the following algorithm be optimized? - c#

Ok, the cards game I'm developing is pretty similar to Scopa if someone knows it.
The deck contains 40 cards divided into 4 different suits of 10 cards each (ace => value 1, two => value 2, three = ..., four, five, six, seven, knave, queen, king => value 10).
There are 2 players (actually an AI and a human player) and they have 4 cards in their hand.
There are 4 free cards to take on the table and players can take them only respecting the following rules:
1) Court cards (knave, queen and king) can only take identical court cards (for example, if I have a queen I can only take a queen from the table).
2) Numeric cards (from ace to seven) can take identical numeric cards or smaller numeric cards by sum (for example, if I have a seven I can take a seven or { an ace, a six } or {a three, a four } or { an ace, three two }).
Now the time has come to find which cards the AI can eventually take during it's turn:
private List<List<Card>> CalculateAITake()
{
List<Int32> handValues = new List<Int32>();
List<List<Card>> takes = new List<List<Card>>();
/* here i take every hand card value, in a unique way
* in order to avoid processing two or more times the
* same value
*/
foreach (Card card in m_AIHand)
{
Int32 cardValue = (Int32)card.Rank;
if (!handValues.Contains(cardValue))
handValues.Add(cardValue);
}
/* for each hand card value now, I calculate the
* combinations of cards I can take from table
*/
foreach (Int32 handValue in handValues)
{
// it's a court card, let's use a direct and faster approach
if (handValue >= 8)
{
foreach (Card card in m_CardsOnTable)
{
if ((Int32)card.Rank == handValue)
{
List<Card> take = new List<Card>();
take.Add(card);
takes.Add(take);
}
}
}
else
// it's a numeric card, let's use recursion
CalculateAITakeRecursion(takes, (new List<Card>(m_CardsOnTable)), 0, (new List<Card>()), handValue, 0);
}
return takes;
}
private void CalculateAITakeRecursion(List<List<Card>> takes, List<Card> cardsExcluded, Int32 cardsExcludedIndex, List<Card> cardsIncluded, Int32 sumWanted, Int32 sumPartial)
{
for (Int32 i = cardsExcludedIndex; i < cardsExcluded.Count; ++i)
{
Card cardExcluded = cardsExcluded[i];
Int32 sumCurrent = sumPartial + (Int32)cardExcluded.Rank;
/* the current sum is lesser than the hand card value
* so I keep on recursing
*/
if (sumCurrent < sumWanted)
{
List<Card> cardsExcludedCopy = new List<Card>(cardsExcluded);
cardsExcludedCopy.Remove(cardExcluded);
List<Card> cardsIncludedCopy = new List<Card>(cardsIncluded);
cardsIncludedCopy.Add(cardExcluded);
CalculateAITakeRecursion(takes, cardsExcludedCopy, ++cardsExcludedIndex, cardsIncludedCopy, sumWanted, sumCurrent);
}
/* the current sum is equal to the hand card value
* we have a new valid combination!
*/
else if (sumCurrent == sumWanted)
{
cardsIncluded.Add(cardExcluded);
Boolean newTakeIsUnique = true;
Int32 newTakeCount = cardsIncluded.Count;
/* problem: sometimes in my results i can find both
* { ace of hearts, two of spades }
* { two of spades, ace of hearts }
* not good, I don't want it to happens because there
* is still a lot of work to do on those results!
* Contains() is not enought to guarantee unique results
* so I have to do this!
*/
foreach (List<Card> take in takes)
{
if (take.Count == newTakeCount)
{
Int32 matchesCount = 0;
foreach (Card card in take)
{
if (cardsIncluded.Contains(card))
matchesCount++;
}
if (newTakeCount == matchesCount)
{
newTakeIsUnique = false;
break;
}
}
}
if (newTakeIsUnique)
takes.Add(cardsIncluded);
}
}
}
Do you think that this algorithm could be improved somehow?
I'm trying to shorten this code as much as I can so that it can be easy to debug and easy to maintain... also, if someone has a more elegant solution to avoid duplicate combinations I would really, really appreciate it (I don't want to get both { ace of hearts, two of spades } and { two of spades, ace of hearts }... only one of them).
Many, many thanks in advance!

Rather than considering each numeric card in your hand and looking for free cards that total it, I would consider each possible total of free cards and looking for a numeric card in your hand that matches it. You could use some sort of bitset to speed up the check for a matching card in your hand, and if you sort the free cards in ascending order, you could avoid adding a card matching one that you skipped, and you could stop adding cards if you exceeded the highest numeric card in your hand.
EDIT: pseudocode follows (sorry, I'm no good at naming variables):
call find_subset_sum(1, List<int>, 0)
// Passing the total because it's easy to calculate as we go
sub find_subset_sum(int value, List<int> play, total)
if total > max_hand_card
return // trying to pick up too many cards
if total in hand_set
call store_play(play)
if value > max_free_card
return // no more cards available to pick up
// try picking up higher value cards only
find_subset_sum(value + 1, play, total)
// now try picking up cards of this value
for each free card
if card value = value // only consider cards of this value
total += value
play.append(card)
find_subset_sum(value + 1, play, total)
// you could remove all the added cards here
// this would avoid having to copy the list each time
// you could then also move the first recursive call here too
It looks a bit odd but that's to ensure that if you only need one card of a particular value you don't unnecessarily consider picking up each available card of that value.
You can optimise this still further by sorting the array in ascending order.

Related

Genetic Algorithm stops mutating

I'm currently trying to make my genetic algorithm "generate" or "evolve" towards an given word. The problem is, that it never fully reaches this word, it stops at an too high fitness score, even if it should continue mutating.
Heres an example:
User input = "HelloWorld"
After 500 generations = "XelgoWorfd"
And I have no clue why it won't continue mutating. Normally it just should resume with changing randomly some chars in the string.
So I would be very glad about some help.
Here's an basic step by step explanation:
Create 20 Chromosomes with fully randomized strings
Calculate the fitness score compared to the goal word.
(Counting Ascii ids differences)
Mate the two Chromosomes with the best score.
Mutate some of the Chromosomes randomly (change random string chars)
Kill 90% of the weak population and replace it with elite chromosomes (The chromosomes with the currently best fitness score).
Repeat everything.
So here the most important methods of my algorithm:
public Chromoson[] mate(string gene) {
Console.WriteLine("[MATING] In Progress : "+gens+" "+gene);
int pivot = (int)Math.Round((double)gens.Length / 2) - 1;
string child1 = this.gens.Substring(0, pivot) + gene.Substring(pivot);
string child2 = gene.Substring(0, pivot) + this.gens.Substring(pivot);
Chromoson[] list = new Chromoson[2];
list[0] = new Chromoson(child1);
list[1] = new Chromoson(child2);
Console.WriteLine("[MATING] Pivot : "+pivot);
Console.WriteLine("[MATING] Children : "+child1+" "+child2);
return list;
}
public void mutate(float chance, int possiblyChanges) {
if (random.Next(0,101) <= chance) return;
int changes = random.Next(0, possiblyChanges + 1);
//int index = (int) Math.Floor((double)random.Next() * this.gens.Length);
for (int i = 0; i < changes; i++) {
int index = random.Next(0, 13);
StringBuilder builder = new StringBuilder(gens);
int upOrDown = random.Next(0, 101);
if (upOrDown <= 50 && (int)builder[index] > 0 && chars.Contains(Convert.ToChar(builder[index] - 1)))
builder[index] = Convert.ToChar(builder[index] - 1);
else if (upOrDown >= 50 && (int)builder[index] < 127 && chars.Contains(Convert.ToChar(builder[index] + 1)))
builder[index] = Convert.ToChar(builder[index] + 1);
else
mutate(chance, possiblyChanges);
gens = builder.ToString();
}
Console.WriteLine("[MUTATING] In Progress");
}
public void calculateCost(string otherGens)
{
int total = 0;
for (int i = 0; i < gens.Length; i++)
{
total += (((int)gens[i] - (int)otherGens[i]) * ((int)gens[i] - (int)otherGens[i])) * (i*i);
}
Console.WriteLine("[CALCULATING] Costs : " + total);
this.cost = total;
}
Something is completely off in your timesteps:
Create 20 Chromosomes with fully randomized strings. Seems okay.
Calculate the fitness score compared to the goal word. (Counting Ascii ids differences). Seems okay.
Mate the two Chromosomes with the best score. What? Your only breeding the two fittest chromosomes to create the new population? That means you will have a population that is nearly completely similar. Breedfitness proportionally, so all genomes have a chance to have an offspring
Mutate some of the Chromosomes randomly (change random string chars)
Kill 90% of the weak population and replace it with elite chromosomes (The chromosomes with the currently best fitness score). You kill 90%? So basically, you're keeping the 2 best genomes every iteration and then replacing the other 18 with step 1? What you want is to keep the 2 fittest at step 3, and create the other 18 individuals by breeding.
Repeat everything.
So change your steps to:
INIT. Initialise population, create 20 random chromosomes
Calculate score for each chromsome
Save the two fittest chromosomes to the next population (aka elitism), getthe other 18 needed individuals by breeding fitness proportionally
Mutate the chromsomes with a certain chance
Repeat
Do not create random individuals every round. This turns your algorithm into a random search.
Your mutate and calculateCost functions are weird. In particular, mutate() looks designed to get trapped in local minimas. Any mutation up or down will be worse than the elites (which are probably identical so crossover changes nothing). Use a different mutate: Pick a random index and change it completely. Also remove i*i from cost().

Algorithm for "consolidating" N items into K

I was wondering whether there's a known algorithm for doing the following, and also wondering how it would be implemented in C#. Maybe this is a known type of problem.
Example:
Suppose I have a class
class GoldMine
{
public int TonsOfGold { get; set; }
}
and a List of N=3 such items
var mines = new List<GoldMine>() {
new GoldMine() { TonsOfGold = 10 },
new GoldMine() { TonsOfGold = 12 },
new GoldMine() { TonsOfGold = 5 }
};
Then consolidating the mines into K=2 mines would be the consolidations
{ {Lines[0],Lines[1]}, {Lines[2]} }, // { 22 tons, 5 tons }
{ {Lines[0],Lines[2]}, {Lines[1]} }, // { 15 tons, 12 tons }
{ {Lines[1],Lines[2]}, {Lines[0]} } // { 17 tons, 10 tons }
and consolidating into K=1 mines would be the single consolidation
{ Lines[0],Lines[1],Lines[2] } // { 27 tons }
What I'm interested in is the algorithm for the consolidation process.
If I'm not mistaken, the problem you're describing is Number of k-combinations for all k
I found a code snippet which I believe addresses your use case but I just can't remember where I got it from. It must have been from StackOverflow. If anyone recognized this particular piece of code, please let me know and I'll make sure to credit it.
So here's the extension method:
public static class ListExtensions
{
public static List<ILookup<int, TItem>> GroupCombinations<TItem>(this List<TItem> items, int count)
{
var keys = Enumerable.Range(1, count).ToList();
var indices = new int[items.Count];
var maxIndex = items.Count - 1;
var nextIndex = maxIndex;
indices[maxIndex] = -1;
var groups = new List<ILookup<int, TItem>>();
while (nextIndex >= 0)
{
indices[nextIndex]++;
if (indices[nextIndex] == keys.Count)
{
indices[nextIndex] = 0;
nextIndex--;
continue;
}
nextIndex = maxIndex;
if (indices.Distinct().Count() != keys.Count)
{
continue;
}
var group = indices.Select((keyIndex, valueIndex) =>
new
{
Key = keys[keyIndex],
Value = items[valueIndex]
})
.ToLookup(x => x.Key, x => x.Value);
groups.Add(group);
}
return groups;
}
}
And a little utility method that prints the output:
public void PrintGoldmineCombinations(int count, List<GoldMine> mines)
{
Debug.WriteLine("count = " + count);
var groupNumber = 0;
foreach (var group in mines.GroupCombinations(count))
{
groupNumber++;
Debug.WriteLine("group " + groupNumber);
foreach (var set in group)
{
Debug.WriteLine(set.Key + ": " + set.Sum(m => m.TonsOfGold) + " tons of gold");
}
}
}
You would use it like so:
var mines = new List<GoldMine>
{
new GoldMine {TonsOfGold = 10},
new GoldMine {TonsOfGold = 12},
new GoldMine {TonsOfGold = 5}
};
PrintGoldmineCombinations(1, mines);
PrintGoldmineCombinations(2, mines);
PrintGoldmineCombinations(3, mines);
Which will produce the following output:
count = 1
group 1
1: 27 tons of gold
count = 2
group 1
1: 22 tons of gold
2: 5 tons of gold
group 2
1: 15 tons of gold
2: 12 tons of gold
group 3
1: 10 tons of gold
2: 17 tons of gold
group 4
2: 10 tons of gold
1: 17 tons of gold
group 5
2: 15 tons of gold
1: 12 tons of gold
group 6
2: 22 tons of gold
1: 5 tons of gold
count = 3
group 1
1: 10 tons of gold
2: 12 tons of gold
3: 5 tons of gold
group 2
1: 10 tons of gold
3: 12 tons of gold
2: 5 tons of gold
group 3
2: 10 tons of gold
1: 12 tons of gold
3: 5 tons of gold
group 4
2: 10 tons of gold
3: 12 tons of gold
1: 5 tons of gold
group 5
3: 10 tons of gold
1: 12 tons of gold
2: 5 tons of gold
group 6
3: 10 tons of gold
2: 12 tons of gold
1: 5 tons of gold
Note: this does not take into account duplicates by the contents of the sets and I'm not sure if you actually want those filtered out or not.
Is this what you need?
EDIT
Actually, looking at your comment it seems you don't want the duplicates and you also want the lower values of k included, so here is a minor modification that takes out the duplicates (in a really ugly way, I apologize) and gives you the lower values of k per group:
public static List<ILookup<int, TItem>> GroupCombinations<TItem>(this List<TItem> items, int count)
{
var keys = Enumerable.Range(1, count).ToList();
var indices = new int[items.Count];
var maxIndex = items.Count - 1;
var nextIndex = maxIndex;
indices[maxIndex] = -1;
var groups = new List<ILookup<int, TItem>>();
while (nextIndex >= 0)
{
indices[nextIndex]++;
if (indices[nextIndex] == keys.Count)
{
indices[nextIndex] = 0;
nextIndex--;
continue;
}
nextIndex = maxIndex;
var group = indices.Select((keyIndex, valueIndex) =>
new
{
Key = keys[keyIndex],
Value = items[valueIndex]
})
.ToLookup(x => x.Key, x => x.Value);
if (!groups.Any(existingGroup => group.All(grouping1 => existingGroup.Any(grouping2 => grouping2.Count() == grouping1.Count() && grouping2.All(item => grouping1.Contains(item))))))
{
groups.Add(group);
}
}
return groups;
}
It produces the following output for k = 2:
group 1
1: 27 tons of gold
group 2
1: 22 tons of gold
2: 5 tons of gold
group 3
1: 15 tons of gold
2: 12 tons of gold
group 4
1: 10 tons of gold
2: 17 tons of gold
This is actually the problem of enumerating all K-partitions of a set of N objects, often described as enumerating the ways to place N labelled objects into K unlabelled boxes.
As is almost always the case, the easiest way to solve a problem involving enumeration of unlabelled or unordered alternatives is to create a canonical ordering and then figure out how to generate only canonically-ordered solutions. In this case, we assume that the objects have some total ordering so that we can refer to them by integers between 1 and N, and then we place the objects in order into the partitions, and order the partitions by the index of the first object in each one. It's pretty easy to see that this ordering cannot produce duplicates and that every partitioning must correspond to some canonical ordering.
We can then represent a given canonical ordering by a sequence of N integers, where each integer is the number of the partition for the corresponding object. Not every sequence of N integers will work, however; we need to constrain the sequences so that the partitions are in the canonical order (sorted by the index of the first element). The constraint is simple: each element in the sequence must either be some integer which previously appeared in the sequence (an object placed into an already present partition) or it must be the index of the next partition, which is one more than the index of the last partition already present. In summary:
The first entry in the sequence must be 1 (because the first object can only be placed into the first partition); and
Each subsequent entry is at least 1 and no greater than one more than the largest entry preceding that point.
(These two criteria could be combined if we interpret "the largest entry preceding" the first entry as 0.)
That's not quite enough, since it doesn't restrict the sequence to exactly K. If we wanted to find all of the partitions, that would be fine, but if we want all the partitions whose size is precisely K then we need to constrain the last element in the sequence to be K, which means that the second last element must be at least K−1, the third last element at least K−2, and so on, as well as not allowing any element to be greater than K:
The element at position i must be in the range [max(1, K+i−N), K]
Generating sequences according to a simple set of constraints like the above can easily be done recursively. We start with an empty sequence, and then successively add each possible next elements, calling this procedure recursively to fill in the entire sequence. As long as it is simple to produce the list of possible next elements, the recursive procedure will be straight-forward. In this case, we need three pieces of information to produce this list: N, K, and the maximum value generated so far.
That leads to the following pseudo-code:
GenerateAllSequencesHelper(N, K, M, Prefix):
if length(Prefix) is N:
Prefix is a valid sequence; handle it
else:
# [See Note 1]
for i from max(1, length(Prefix) + 1 + K - N)
up to min(M + 1, K):
Append i to Prefix
GenerateAllSequencesHelper(N, K, max(M, i), Prefix)
Pop i off of Prefix
GenerateAllSequences(N, K):
GenerateAllSequencesHelper(N, K, 0, [])
Since the recursion depth will be extremely limited for any practical application of this procedure, the recursive solution should be fine. However, it is also quite simple to produce an iterative solution even without using a stack. This is an instance of a standard enumeration algorithm for constrained sequences:
Start with the lexicographically smallest possible sequence
While possible:
Scan backwards to find the last element which could be increased. ("Could be" means that increasing that element would still result in the prefix of some valid sequence.)
Increment that element to the next largest possible value
Fill in the rest of the sequence with the smallest possible suffix.
In the iterative algorithm, the backwards scan might involve checking O(N) elements, which apparently makes it slower than the recursive algorithm. However, in most cases they will have the same computational complexity, because in the recursive algorithm each generated sequence also incurs the cost of the recursive calls and returns required to reach it. If each (or, at least, most) recursive calls produce more than one alternative, the recursive algorithm will still be O(1) per generated sequence.
But in this case, it is likely that the iterative algorithm will also be O(1) per generated sequence, as long as the scan step can be performed in O(1); that is, as long as it can be performed without examining the entire sequence.
In this particular case, computing the maximum value of the sequence up to a given point is not O(1), but we can produce an O(1) iterative algorithm by also maintaining the vector of cumulative maxima. (In effect, this vector corresponds to the stack of M arguments in the recursive procedure above.)
It's easy enough to maintain the M vector; once we have it, we can easily identify "incrementable" elements in the sequence: element i is incrementable if i>0, M[i] is equal to M[i−1], and M[i] is not equal to K. [Note 2]
Notes
If we wanted to produce all partitions, we would replace the for loop above with the rather simpler:
for i from 1 to M+1:
This answer is largely based on this answer, but that question asked for all partitions; here, you want to generate the K-partitions. As indicated, the algorithms are very similar.

List cast to int in a Deck Shuffling program [duplicate]

This question already has answers here:
Card Shuffling in C#
(9 answers)
Closed 7 years ago.
I'm currently trying to write my first program so my coding isn't nearly as knowledgeable as others...
I am trying to shuffle a deck of cards without using the exact code given in the Fischer-Yates method or at least my version of it...
My plan is to to assign the cards to an enum and that will assign them a number from 0-51. Then I set the cards in a list and remove the card from the list that the RNG generates. After the card is removed from the list the counter deletes one from the possible random numbers and I set the number to a temporary variable that I will then pass to a new list that will be the shuffled deck.
My issue is in finding code that will explicitly convert my first list to an int. I think I know how to remove from the first list using .RemoveAt. Will I need to make another conversion or can i store the Random Number I find into the shuffledDeck?
enum Cards
{
AceSpade,
TwoSpade,
ThreeSpade,
FourSpade,
FiveSpade,
SixSpade,
SevenSpade,
EightSpade,
NineSpade,
TenSpade,
JackSpade,
QueenSpade,
KingSpade,
AceHeart,
TwoHeart,
ThreeHeart,
FourHeart,
FiveHeart,
SixHeart,
SevenHeart,
EightHeart,
NineHeart,
TenHeart,
JackHeart,
QueenHeart,
KingHeart,
AceDiamond,
TwoDiamond,
ThreeDiamond,
FourDiamond,
FiveDiamond,
SixDiamond,
SevenDiamond,
EightDiamond,
NineDiamond,
TenDiamond,
JackDiamond,
QueenDiamond,
KingDiamond,
AceClub,
TwoClub,
ThreeClub,
FourClub,
FiveClub,
SixClub,
SevenClub,
EightClub,
NineClub,
TenClub,
JackClub,
QueenClub,
KingClub
}
class DeckShuffle
{
static Random rndCard = new Random(DateTime.Now.Millisecond);
int currentCard;
int tempCard;
int totalCards = 51;
List<Cards> currentDeck = new List<Cards>();
List<int> shuffledDeck = new List<int>();
private void CardShuffler()
{
while (totalCards > -1)
{
tempCard = rndCard.Next(0, 51);
totalCards--;
}
}
}
So you have a list of cards, currentDeck.
I'd recommend a for loop to have an integer value like:
List<Cards> currentDeck = new List<Cards>();
List<Cards> shuffledDeck = new List<Cards>();
for(int i = 0; i < currentDeck.Count - 1; i++) {
//Grab a card, insert it into ShuffledDeck
int randoNum = rndCard.Next(0, 51-i); //This will grab a new number between 1 and however many cards are left
shuffledDeck.Add(currentDeck[randoNum]);
currentDeck.RemoveAt(i);
}
This way, every loop as i increases, the number of cards left in currentDeck decreases, and we grab one, and add it to the shuffled deck.
Edit: I'd probably recommend creating a Card class though (if you're that far into learning C#) - each "Card" object would have a numeric value (1-13, 11 for Jack, 12 for Queen, 13 for King, etc) and a suit.
That'll make most card-based games a lot easier.

how to generate a random and then not show that random again

I'm writing a card game in C#, a game of blackjack, where the user presses a button and it deals a card, the value of that card gets added to a text box. If the value of the text box goes over 21 the user has lost.
I'm having a problems with the dealing of the cards, i generate a random, say 5 gets generated show the 5 of diamonds, add the value of the 5 of diamonds to the score text box and display a picture box(the 5 of diamonds playing card). However once 5 has been generated i'm having problems not generating that card again, i have the logic down of what needs to happen, i'm just not sure on the actual code itself as i'm a beginner.
So far iv tried 2 different ways, a List and a array of Boolean values and i'm still struggling, could anybody point me in the right direction in terms of code.
List<int> Diamonds = new List<int>();
Random random = new Random();
genRandom = random.Next(0, 5);
while (Diamonds.Contains(genRandom))
{
genRandom = random.Next(0, 5);
break;
}
while (!Diamonds.Contains(genRandom))
if (genRandom == 0)
{
Diamonds.add(0);
score = score += 2;
scoreTextBox.Text = score.ToString();
diamonds2.Show();
}
Thanks in advance, sorry about the bad grammar!
I would take the reverse approach, by creating a collection that holds all the possible cards, and then draw them from the collection randomly.
Say you have a class called Deck, which represents a card deck. Fill it with Card classes. Now when you start drawing cards, randomly pick a number in the collection and remove that card from the Deck. The next time the same random number is drawn, it will draw a different card from the deck, since you remove the used cards.
Just remember to generate a random number that is within bounds of the size of the deck, which will decrease after each draw.
The problem you have right now is that you don't have a card pool. You should have a list of card you can draw from and once a card is picked, it is removed from the available choices and cannot be drawn again.
List<Card> deck = new List<Card>();
deck.Add(new Card(1, CardType.Diamonds));
deck.Add(new Card(2, CardType.Diamonds));
...
Card nextCard = deck[Random.Next(0, deck.Count - 1)];
deck.Remove(nextCard);
where:
struct Card
{
public int number;
public CardType type;
public Card(int number, CardType type)
{
this.number = number;
this.type = type;
}
}
enum CardType
{
Diamonds,
Spades,
Hearts,
Clubs
}
It's a very simplistic, object oriented approach where each card is clearly defined as a unique container. It might not be the most optimal way, but probably much easier to understand.
There are many ways to do that the most i use it is as follows:
List<int> available = new List<int>(5000);
for(int i=1; i<=5; i++)
available.Add(i);
The above code will generate all random numbers.
Now you can choose from them as follows:
List<int> result = new List<int>(5000);
while(available.Count > 0)
{
int index = random.Next(availableCount);
result.Add(available[index]);
available.RemoveAt(index);
}
return result;
Since you are removing after getting they will never repeat.
You could do something like this:
List<int> Diamonds = new List<int>();
for(int i = 1; i <= 10; i++) //10 is just an example..dk how many cards :P
{
Diamonds.Add(i);
}
Random random = new Random();
int index = random.Next(0, Diamonds.Count - 1);
int nr = Diamonds[index];
Diamonds.Remove(index);
Easiest thing to do would be to sort the entire list randomly (by sorting by Rand() - 0.5), then each time you want a 'random' card, take the first one and remove it from the list. The cards are in a random order so it's the same effect statistically as picking a random one each time, and you won't pick the same card twice in a given game.
The easiest way to do this conceptually is to have an array which represents a deck of cards, and then deal cards from it.
Ideally you would use a stack for this, but unfortunately you can't shuffle a stack!
Therefore, your best bet is to use a List<Card>, where 'Card' is a class that represents a card. The Card class would have two properties: a Suite and a rank from 1 (the Ace) to 13 where 11 is Jack, 12 is Queen and 13 is King.
You would populate your List<Card> deck with all 52 possible cards, and then you could shuffle it using an implementation of the Fisher-Yates shuffle.
Here's a complete sample:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var deck = new List<Card>();
for (int rank = 1; rank <= 13; ++rank)
foreach (Suite suite in Enum.GetValues(typeof(Suite)))
deck.Add(new Card {Rank = rank, Suite = suite});
var shuffler = new Shuffler();
shuffler.Shuffle(deck);
// Deal the top 10 cards.
for (int i = 0; i < 10; ++i)
Console.WriteLine(deck[i]);
}
}
public enum Suite
{
Clubs,
Diamonds,
Hearts,
Spades
}
public sealed class Card
{
public Suite Suite;
public int Rank;
public override string ToString()
{
string rank;
switch (Rank)
{
case 1: rank = "Ace"; break;
case 11: rank = "Jack"; break;
case 12: rank = "Queen"; break;
case 13: rank = "King"; break;
default: rank = Rank.ToString(); break;
}
return string.Format("{0} of {1}", rank, Suite);
}
}
public sealed class Shuffler
{
public Shuffler()
{
_rng = new Random();
}
public void Shuffle<T>(IList<T> array)
{
for (int n = array.Count; n > 1; )
{
int k = _rng.Next(n);
--n;
T temp = array[n];
array[n] = array[k];
array[k] = temp;
}
}
private readonly Random _rng;
}
}
Real code should validate Rank and Suite, of course. Also, you'd be better off writing a Deck class which encapsulated shuffling and dealing (it could remove cards from the end of the List<Card> when dealing).

Help with maths/coding on possible combinations of a set to make up a total - C#

I have a coding/maths problem that I need help translating into C#. It's a poker chip calculator that takes in the BuyIn, the number of players and the total amount of chips for each colour (there are x amount of colours) and their value.
It then shows you every possible combination of chips per person to equal the Buy In. The user can then pick the chipset distribution they would like to use. It's best illustrated with a simple example.
BuyIn: $10
Number of Players: 1
10 Red Chips, $1 value
10 Blue Chips, $2 value
10 Green Chips, $5 value
So, the possible combinations are:
R/B/G
10/0/0
8/1/0
6/2/0
5/0/1
4/3/0
2/4/0
1/2/1
etc.
I have spent a lot of time trying to come up with an algorithm in C#/.NET to work this out. I am stumbling on the variable factor - there's usually only 3 or 4 different chips colours in a set, but there could be any amount. If you have more than one player than you have to count up until TotalChips / NumberOfPlayers.
I started off with a loop through all the chips and then looping from 0 up to NumberOfChips for that colour. And this is pretty much where I have spent the last 4 hours... how do I write the code to loop through x amount of chips and check the value of the sum of the chips and add it to a collection if it equals the BuyIn? I need to change my approach radically methinks...
Can anyone put me on the right track on how to solve this please? Pseudo code would work - thank you for any advice!
The below is my attempt so far - it's hopeless (and wont compile, just an example to show you my thought process so far) - Might be better not to look at it as it might biased you on a solution...
private void SplitChips(List<ChipSuggestion> suggestions)
{
decimal valueRequired = (decimal)txtBuyIn.Value;
decimal checkTotal = 0;
ChipSuggestion suggestion;
//loop through each colour
foreach (Chip chip in (PagedCollectionView)gridChips.ItemsSource)
{
//for each value, loop through them all again
foreach (Chip currentChip in (PagedCollectionView)gridChips.ItemsSource)
{
//start at 0 and go all the way up
for (int i = 0; i < chip.TotalChipsInChipset; i++)
{
checkTotal = currentChip.ChipValue * i;
//if it is greater than than ignore and stop
if (checkTotal > valueRequired)
{
break;
}
else
{
//if it is equal to then this is a match
if (checkTotal == valueRequired)
{
suggestion = new ChipSuggestion();
suggestion.SuggestionName = "Suggestion";
chipRed.NumberPerPlayer = i;
suggestion.Chips.Add(chipRed);
chipBlue.NumberPerPlayer = y;
suggestion.Chips.Add(chipBlue);
chipGreen.NumberPerPlayer = 0;
suggestion.Chips.Add(chipGreen);
//add this to the Suggestion
suggestions.Add(suggestion);
break;
}
}
}
}
}
}
Here's an implementation that reads the number of chips, the chips (their worth and amount) and the buyin and displays the results in your example format. I have explained it through comments, let me know if you have any questions.
class Test
{
static int buyIn;
static int numChips;
static List<int> chips = new List<int>(); // chips[i] = value of chips of color i
static List<int> amountOfChips = new List<int>(); // amountOfChips[i] = number of chips of color i
static void generateSolutions(int sum, int[] solutions, int last)
{
if (sum > buyIn) // our sum is too big, return
return;
if (sum == buyIn) // our sum is just right, print the solution
{
for (int i = 0; i < chips.Count; ++i)
Console.Write("{0}/", solutions[i]);
Console.WriteLine();
return; // and return
}
for (int i = last; i < chips.Count; ++i) // try adding another chip with the same value as the one added at the last step.
// this ensures that no duplicate solutions will be generated, since we impose an order of generation
if (amountOfChips[i] != 0)
{
--amountOfChips[i]; // decrease the amount of chips
++solutions[i]; // increase the number of times chip i has been used
generateSolutions(sum + chips[i], solutions, i); // recursive call
++amountOfChips[i]; // (one of) chip i is no longer used
--solutions[i]; // so it's no longer part of the solution either
}
}
static void Main()
{
Console.WriteLine("Enter the buyin:");
buyIn = int.Parse(Console.ReadLine());
Console.WriteLine("Enter the number of chips types:");
numChips = int.Parse(Console.ReadLine());
Console.WriteLine("Enter {0} chips values:", numChips);
for (int i = 0; i < numChips; ++i)
chips.Add(int.Parse(Console.ReadLine()));
Console.WriteLine("Enter {0} chips amounts:", numChips);
for (int i = 0; i < numChips; ++i)
amountOfChips.Add(int.Parse(Console.ReadLine()));
int[] solutions = new int[numChips];
generateSolutions(0, solutions, 0);
}
}
Enter the buyin:
10
Enter the number of chips types:
3
Enter 3 chips values:
1
2
5
Enter 3 chips amounts:
10
10
10
10/0/0/
8/1/0/
6/2/0/
5/0/1/
4/3/0/
3/1/1/
2/4/0/
1/2/1/
0/5/0/
0/0/2/
Break the problem down recursively by the number of kinds of chips.
For the base case, how many ways are there to make an $X buy-in with zero chips? If X is zero, there is one way: no chips. If X is more than zero, there are no ways to do it.
Now we need to solve the problem for N kinds of chips, given the solution for N - 1. We can take one kind of chip, and consider every possible number of that chip up to the buy-in. For example, if the chip is $2, and the buy-in is $5, try using 0, 1, or 2 of them. For each of these tries, we have to use only the remaining N - 1 chips to make up the remaining value. We can solve that by doing a recursive call, and then adding our current chip to each solution it returns.
private static IEnumerable<IEnumerable<Tuple<Chip, int>>> GetAllChipSuggestions(List<Chip> chips, int players, int totalValue)
{
return GetAllChipSuggestions(chips, players, totalValue, 0);
}
private static IEnumerable<IEnumerable<Tuple<Chip, int>>> GetAllChipSuggestions(List<Chip> chips, int players, int totalValue, int firstChipIndex)
{
if (firstChipIndex == chips.Count)
{
// Base case: we have no chip types remaining
if (totalValue == 0)
{
// One way to make 0 with no chip types
return new[] { Enumerable.Empty<Tuple<Chip, int>>() };
}
else
{
// No ways to make more than 0 with no chip types
return Enumerable.Empty<IEnumerable<Tuple<Chip, int>>>();
}
}
else
{
// Recursive case: try each possible number of this chip type
var allSuggestions = new List<IEnumerable<Tuple<Chip, int>>>();
var currentChip = chips[firstChipIndex];
var maxChips = Math.Min(currentChip.TotalChipsInChipset / players, totalValue / currentChip.ChipValue);
for (var chipCount = 0; chipCount <= maxChips; chipCount++)
{
var currentChipSuggestion = new[] { Tuple.Create(currentChip, chipCount) };
var remainingValue = totalValue - currentChip.ChipValue * chipCount;
// Get all combinations of chips after this one that make up the rest of the value
foreach (var suggestion in GetAllChipSuggestions(chips, players, remainingValue, firstChipIndex + 1))
{
allSuggestions.Add(suggestion.Concat(currentChipSuggestion));
}
}
return allSuggestions;
}
}
For some large combinations this is propably not solvable in finite time.
(It is a NP problem)
http://en.wikipedia.org/wiki/Knapsack_problem
There are also links with Code? that could help you.
Hope this helps a bit.

Categories