Dice Question (Full House and Straight recognition) - c#

I'm making a dice game. There are 5 dice in which I have all the values for and I need to determine if they make up a full house (3 of one and 2 of another), small straight (1-4, 2-6 or 3-6) or a large straight (1-5, 2-6).
Perhaps the best way to approach this seems to be to use regular expressions.
Does anyone know how I would go about representing these rules in regex?
Or if you can provide a better solution, I'd appreciate that.
Examples:
Full house = 44422 or 11166 or 12212 etc.
Small Straight = 12342 or 54532 etc.
Large Straight = 12345 or 52643 etc.
Edit
Changed wording to highlight that this is my inexperienced opinion.
I know how to achieve this using code, but it seems like such a long winded solution, I'm wondering if there's a more simplistic approach.

I would order all the numbers decreasing and then do some linear criteria matching on each value as you go along it whether it be in an int[] or a string.

Don't know about c#, but in a scripting language I'd take the regexp route. For each side, calculate how many times it occurs in the combination and join the results together. For example, for the combination 12342 the counter string will be 121100. Then match the counter string against these patterns:
/5/ = Five of a kind
/4/ = Four of a kind
/20*3|30*2/ = Full house
/1{5}/ = Large Straight
/[12]{4}/ = Small Straight
/3/ = Three of a kind
/2[013]*2/ = Two pair
/2/ = One pair

You could always do a LINQ aggregate query and count the number of same cards. It would be something similar to (can't test it):
var query = from card in hand
group card by card into groupedCards
select new { Card = groupedCards.Key, Count = groupedCards.Count() };
This way you would easily know if you are dealing with a possible straight (or nothing at all), a pair, a triple, etc.
I am no LINQ expert and I can not test this bit of code at the moment, so I am not sure if it will compile as it is, but it might help you or give you an idea on how to approach the problem at hand.
For example:
if count query = 5 : We are dealing with an empty hand, a flush or a straight => Run particular flush/straight logic.
if count query = 4 : We are dealing with a single pair.
if count query = 3 : We are dealing with a double pair or a triple => if max count =3 then triple
if count query = 2 : We are dealing with a full house / poker. If max count = 4 then poker

I won't comment on how you seek the results, but rather on how you store the result for later lookup.
Since you have only 46656 possible combinations and one byte can store the resulting hand strength, this problem is much easier than a poker hand problem.
You can have a lookup table, consisting of hands as indexes and associated with results (some hands can have multiple results) of that hand. Each byte can store all hand types as a binary representation (hopefully, if not use a short).
Each number you get (eg. 66655 - full house) is a number in base six (1-6), convert it into a base 10 number to get the index in the lookup table.
It will require about 46656 bytes (+ CPU alignment), and can fit into CPU L2 cache. Speed would be enourmous, since the only operation you would need to do is convert number base, and the binary OR operation to extract a hand strenght.
What you will miss is the real strength of a hand. Eg. 66655 is better than 66644. You can easily figure that out - you will need a bigger type to store result into :)

I decided to try myself, and I ended up not using regular expressions -- I thought maybe with the simplicity of the searches required, regular expressions would add more complexity than they save. I used similar logic to another answer though: count the quantity of each number and base all the scoring on that:
enum RollScoreType
{
HighDie,
Pair,
TwoPair,
ThreeOfAKind,
SmallStright,
PairSmallStriaght,
FullHouse,
LargeStraight,
FourOfAKind,
FiveOfAKind
}
struct RollScore
{
public RollScoreType type;
public byte highestDie;
public byte nextHighestDie;
public RollScore(RollScoreType type, byte highest, byte next)
{
this.type = type;
this.highestDie = highest;
this.nextHighestDie = next;
}
public override string ToString()
{
return string.Format("{0} {1} {2}", type, highestDie, nextHighestDie);
}
}
static RollScore GetDiceScore(string input)
{
char[] dice = input.ToCharArray();
byte[] diceCount = new byte[6];
for (int i = 0; i < dice.Length; i++)
diceCount[int.Parse(dice[i].ToString())-1]++;
if (Array.IndexOf(diceCount, (byte)5) >= 0)
return new RollScore(RollScoreType.FiveOfAKind, (byte)(Array.IndexOf(diceCount, (byte)5) + 1), 0);
else if (Array.IndexOf(diceCount, (byte)4) >= 0)
return new RollScore(RollScoreType.FourOfAKind, (byte)(Array.IndexOf(diceCount, (byte)4) + 1), (byte)(Array.IndexOf(diceCount, (byte)1) + 1));
else if (Array.IndexOf(diceCount, (byte)3) >= 0)
{
byte three = (byte)(Array.IndexOf(diceCount, (byte)3) + 1);
if (Array.IndexOf(diceCount, (byte)2) >= 0)
{
byte pair = (byte)(Array.IndexOf(diceCount, (byte)2) + 1);
return new RollScore(RollScoreType.FullHouse, Math.Max(pair, three), Math.Min(pair, three));
}
else
return new RollScore(RollScoreType.ThreeOfAKind, three, (byte)(Array.LastIndexOf(diceCount, (byte)1) + 1));
}
else if (Array.IndexOf(diceCount, (byte)2) >= 0)
{
byte pair = (byte)(Array.IndexOf(diceCount, (byte)2) + 1);
byte highPair = (byte)(Array.LastIndexOf(diceCount, (byte)2) + 1);
if (highPair != pair)
return new RollScore(RollScoreType.TwoPair, highPair, pair);
else
{
byte lowMissingDie = (byte)Array.IndexOf(diceCount, (byte)0);
byte highMissingDie = (byte)Array.LastIndexOf(diceCount, (byte)0);
switch (lowMissingDie)
{
case 0:
if (highMissingDie == 5)
return new RollScore(RollScoreType.PairSmallStriaght, 5, 4);
if (highMissingDie == 1)
return new RollScore(RollScoreType.PairSmallStriaght, 6, 5);
break;
case 4:
return new RollScore(RollScoreType.PairSmallStriaght, 4, 3);
}
return new RollScore(RollScoreType.Pair, pair, (byte)(Array.LastIndexOf(diceCount, (byte)1) + 1));
}
}
byte missingDie = (byte)Array.IndexOf(diceCount, (byte)0);
switch(missingDie)
{
case 0:
return new RollScore(RollScoreType.LargeStraight, 6, 5);
case 1:
return new RollScore(RollScoreType.SmallStright, 6, 5);
case 4:
return new RollScore(RollScoreType.SmallStright, 4, 3);
case 5:
return new RollScore(RollScoreType.LargeStraight, 5, 4);
default:
return new RollScore(RollScoreType.HighDie, 6, (byte)(Array.LastIndexOf(diceCount, (byte)1, 3) + 1));
}
}
I discovered, to my surprise, that the probability of a small straight and a large straight are equal in 5-die rolls. Is that true!?
EDIT: Fixed; I see that when I include small straights that include a pair, the probability of a small straight goes up significantly.
When I think about it, a pair and a small straight should probably use the pair as the highest die and the highest number in the straight as the next highest (in order to [properly compare two rolls that are both a pair with a small straight). If so, I'd replace the block of code for handling PairSmallStraight with this:
switch (lowMissingDie)
{
case 0:
if (highMissingDie == 5)
return new RollScore(RollScoreType.PairSmallStriaght, pair, 5);
if (highMissingDie == 1)
return new RollScore(RollScoreType.PairSmallStriaght, pair, 6);
break;
case 4:
return new RollScore(RollScoreType.PairSmallStriaght, pair, 4);
}

You could try to put your values in to a list. This would allow you to quickly sort your values. And if you add the values that would give you the hand. 111AA = 29 and 222KK = 30. Just an idea.

Here is my Code:
public static int CalculateTotalOfSingles (int pipNumber)
{
//
var myScore = 0;
foreach (var myDie in Roll5Player.MyHand.Dice)
{
{ if (myDie.Pips == pipNumber)
myScore+= pipNumber;
}
}
//
return myScore;
}
public static int CalculateDicePips ()
{
//
var myScore = 0;
foreach (var myDie in Roll5Player.MyHand.Dice)
{
{myScore += myDie.Pips;
}
}
//
return myScore;
}
//
//
//
public static int CalculateTotalOfAllSingles (int pipNumber)
{
//
var myScore = 0;
for (int i = 1; i <= 6; i++)
{
myScore += pipNumber;
}
//
return myScore;
}
public static bool CalculateIsNOfaKind (int count)
{
//
for (var index = 1; index <= 6; index++)
{
var cntr = 0;
foreach (var myDie in Roll5Player.MyHand.Dice)
{
if (myDie.Pips == index)
cntr++;
}
//
if (cntr == count)
{
return true;
;
}
}
//
return false;
}
public static int CalculateNOfaKind (int count )
{
//
var myScore = 0;
for (var index = 1; index <= 6; index++)
{
var cntr = 0;
foreach (var myDie in Roll5Player.MyHand.Dice)
{
if (myDie.Pips == index)
cntr++;
}
//
if (cntr >= count)
{ myScore = CalculateDicePips();
return myScore;
;
}
}
//
return myScore;
}
///
public static int CaluclateFullHouse ( )
{
//
var myScore = 0;
var cntr = new int[6];
for (var index = 1; index <= 6; index++)
{
foreach (var myDie in Roll5Player.MyHand.Dice)
{
if (myDie.Pips == index)
cntr[index-1]++;
}
}
//
var boolCondA = false;
var boolCondB = false;
foreach (var i in cntr)
{
if (i == 3)
{boolCondA = true;
break;
}
}
if (boolCondA)
{
foreach (var i in cntr)
{
if (i == 2)
{boolCondB = true;
break;
}
}
}
//
if (boolCondB )
myScore = CalculateDicePips();
//
//
//
return myScore;
}
public static int CaluclateStraights (int straightCount, int score)
{
//
var tempPip = 0;
var myScore = 0;
var isFirstIteration = true;
var cntr = 0;
int[] sortedDice = new int[5];
var sortedDiceLise = new List<int>();
foreach (var myDie in Roll5Player.MyHand.Dice)
{
sortedDiceLise.Add(myDie.Pips);
}
sortedDiceLise.Sort();
foreach (var myDie in sortedDiceLise)
{
//
//
if (!isFirstIteration)
{
if (myDie == tempPip + 1)
cntr++;
}
//
isFirstIteration = false;
tempPip = myDie;
}
if (cntr == straightCount - 1)
{myScore = score;
}
//
//
//
return myScore;
}
public static int CalculateYahtzee ()
{
//
for (var index = 1; index <= 6; index++)
{
var cntr = 0;
foreach (var myDie in Roll5Player.MyHand.Dice)
{
if (myDie.Pips == index)
cntr++;
}
//
if (cntr == 5)
{
return 50;
;
}
}
//
return 0;
}

Related

find the first element of an array that is not consecutive using web forms

E.g. If we have an array [1,2,3,4,6,7,8] then 1 then 2 then 3 then 4 are all consecutive but 6 is not, so that's the first non-consecutive number.
If the whole array is consecutive then return null .
The array will always have at least 2 elements 1 and all elements will be numbers. The numbers will also all be unique and in ascending order. The numbers could be positive or negative and the first non-consecutive could be either too. please help me finish this code i am new in programming. My code:
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _2katas
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var input = this.txtInput.Text;
var numarray = input.Split(',');
int firstValue = Convert.ToInt32(numarray[0]);
for (var i = 0; i < numarray.Length; i++)
{
if (Convert.ToInt32(numarray[i]) - i != firstValue)
{
lblPrint.Text = "";
}
else
{
lblPrint.Text = "";
}
if (this.rdbConsecutive.Checked == true)
{
lblKataRunning.Text = "Consecutive";
}
else if (this.rdbStripCleaning.Checked == true)
{
lblKataRunning.Text = "Strip Cleaning";
}
}
}
}
}
Let's extract a method:
Find the first element of an array that is not consecutive ...
If the whole array is consecutive then return null
We can implement it like this:
private static string FirstInconsecutive(string data) {
var array = data.Split(',');
if (array.Length <= 0)
return null; //TODO: what shall we return on empty array?
if (!int.TryParse(array[0], out int delta))
return array[0];
for (int i = 1; i < array.Length; ++i)
if (!int.TryParse(array[i], out int value) || value - i != delta)
return array[i];
return null;
}
Usage:
string result = FirstInconsecutive(txtInput.Text);
Please note int.TryParse which helps to return the right answer "ABC" on an input like "1, 2, 3, ABC, 4, 6, 7, 8" (user input txtInput.Text can contain any string)
A linq solution just for the fun of it:
static int? FindFirstNonConsecutive(IEnumerable<int> arr)
{
var nonConsecutiveInfo =
arr.Select((i, index) => new {Index = index, Delta = i - index})
.FirstOrDefault(t => t.Delta != arr.First());
return nonConsecutiveInfo?.Delta + nonConsecutiveInfo?.Index;
}
Note that this will only work finding non consecutive numbers in ascending order as per requirements.
Two numbers are not consecutive if the left ones + 1 <> the right one.
Check with something like this, note that you have to change your boundary checks:
for (var i = 0; i < numarray.Length - 1; i++)
{
if (Convert.ToInt32(numarray[i]) + 1 != Convert.ToInt32(numarray[i+1]))
Update your condition as below for loop and it will work. I would suggest you to have separate function so that it could be reusable if needed elsewhere in code.
Here start your loop from i = 1 and compare numarray[i-1] + 1 != numarray[i] values.
You can convert your sting[] to int[] with var numarray = input.Split(',').Select(x => Convert.ToInt32(x)).ToArray(); and use it with IsConsecutive(numarray) as per button1_Click code.
You can get first non-consecutive value with minor modification in return type and return statement as shown in GetFirstNonConsecutiveValue().
public bool IsConsecutive(int[] numarray)
{
for (int i = 1; i < numarray.Length; i++)
{
if (numarray[i-1] + 1 != numarray[i])
{
return false;
}
}
return true;
}
public int? GetFirstNonConsecutiveValue(int[] numarray)
{
for (int i = 1; i < numarray.Length; i++)
{
if (numarray[i-1] + 1 != numarray[i])
{
return numarray[i];
}
}
return null;
}
private void button1_Click(object sender, EventArgs e)
{
var input = this.txtInput.Text;
var numarray = input.Split(',').Select(x => Convert.ToInt32(x)).ToArray();
var nonConsecutiveValue = GetFirstNonConsecutiveValue(numarray);
if (nonConsecutiveValue != null)
{
// nonConsecutiveValue is first non consecutive value.
}
else
{
// sequence is consecutive.
}
}
One way to go.
string rawData = "1,2,3,4,6,7,8,9";
IEnumerable<int> data = rawData.Split(',').Select(v => Convert.ToInt32(v));
IEnumerable<int> all = Enumerable.Range(data.Min(), data.Max() - data.Min() + 1);
IEnumerable<int> diff = all.Except(data);
if (diff.Count() == 0)
{
return null;
}
return data.ElementAt(all.ToList().IndexOf(diff.First()))
NB Not thoroughly tested.
Just test diff for being empty to get the numbers are consecutive

Get permutation of specific characters in strings

Given a string like "N00MNM" I need all permutations of zero '0' char inside the string maintaining all other chars in fixed order.
The result must be:
"N0M0NM" "N0MN0M" "N0MNM0" "NM00NM" "NM0N0M" "NM0NM0" "NMN0M0" "NMNM00"
"0N0MNM" "0NM0NM" "0NMN0M" "0NMNM0"
Standard permutation function takes too time to do that work (we are talking of about 1500ms) and strings to test are longer than the sample one.
There's an algorithm for this?
What you're trying to do can be done by getting all different positions in which the character 0 (in this case) can be placed and then including the total of 0 characters (00 in this case) in all positions of the string. These positions are taken from the string without all occurrences of 0. The code bellow does it:
public static IEnumerable<string> Combs(string str, char c)
{
int count = str.Count(_c => _c == c);
string _str = new string(str.Where(_c => _c != c).ToArray());
// Compute all combinations with different positions
foreach (var positions in GetPositionsSets(0, _str.Length, count))
{
StringBuilder _b = new StringBuilder();
int index = 0;
foreach (var _char in _str)
{
if (positions.Contains(index))
{ _b.Append($"{c}{_char}"); }
else
{ _b.Append(_char); }
index++;
}
if (positions.Contains(index))
_b.Append(c);
yield return _b.ToString();
}
//Compute the remaining combinations. I.e., those whose at some position
//have the amount of supplied characters.
string p = new string(c, count);
for (int i = 0; i < _str.Length; i++)
{
yield return _str.Insert(i, p);
}
yield return _str + p;
}
//Gets all posible positions sets that can be obtain from minPos
//until maxPos with positionsCount positions, that is, C(n,k)
//where n = maxPos - minPos + 1 and k = positionsCount
private static IEnumerable<HashSet<int>> GetPositionsSets(int minPos, int maxPos, int positionsCount)
{
if (positionsCount == 0)
yield return new HashSet<int>();
for (int i = minPos; i <= maxPos; i++)
{
foreach (var positions in GetPositionsSets(i + 1, maxPos, positionsCount - 1))
{
positions.Add(i);
yield return positions;
}
}
}
The output of the code above for "N00MNM" is:
0N0MNM
0NM0NM
0NMN0M
0NMNM0
N0M0NM
N0MN0M
N0MNM0
NM0N0M
NM0NM0
NMN0M0
00NMNM
N00MNM
NM00NM
NMN00M
NMNM00

Comparing rapidly-changing string values to find frequent occurrences

My problem should be quite simple. I have a random generated string that changes multiple times per second. I want to return 1 if it appears x times consecutively.
My current code:
string s; //this is the generated string
int checker = 0;
string[] s_list = null;
if( cheaker == 0)
{
s_list[0] = s;
}
else if( cheaker == 1)
{
s_list[1] = s;
}
checker++;
if(s_list[0] == s_list[1]) return 1;
My problem is that I want to be able to change the amount of x times if appears and like this it will generate tons of code if the x is too big.
Do you think putting the current string into an array of string and compare them is the best way? There should be a better implementation.
To make the code generic for any given X, you should keep last X strings and check whether all they are equal, e.g.:
List<string> list = new List<string>();
if (list.Count >= X)
{
list.RemoveAt(0);
}
list.Add(newString);
return list.Count >= X && list.Any(s => s == list[0]);
I assume s_list is an array where you store all the generations of the string
string s;
string[] s_list;
// Your logic that would generate s and store it in s_list
// ...
// ...
int required_amount = 10; // whatever X amount you want
int current_sequence = 0;
// It's important to start at 1, and not 0,
// as you compare to the previous entry each time
for(int i = 1; i < s_list.Lenght; i++ )
{
if( s_list[i] == s_list[i-1] )
{
current_sequence++;
if(current_sequence >= required_amount)
{
return 1;
}
}
else
{
current_sequence = 0;
}
}

How to get all unique posiblity's out of a list?

For my problem I have a list of a count larger then 6+. From that list I would like to make a list containing every possible combination of the original cards that is exactly 6 cards long. (they have to be unique and order doesn't matter)
so object
01,02,03,04,05,06
is the same for me as
06,05,04,03,02,01
//STARTER list with more then 6 value's
List < ClassicCard > lowCardsToRemove = FrenchTarotUtil.checkCountLowCardForDiscardChien(handCards);
The solution i found and used:
public static List generateAllSubsetCombinations(object[] fullSet, ulong subsetSize) {
if (fullSet == null) {
throw new ArgumentException("Value cannot be null.", "fullSet");
}
else if (subsetSize < 1) {
throw new ArgumentException("Subset size must be 1 or greater.", "subsetSize");
}
else if ((ulong)fullSet.LongLength < subsetSize) {
throw new ArgumentException("Subset size cannot be greater than the total number of entries in the full set.", "subsetSize");
}
// All possible subsets will be stored here
List<object[]> allSubsets = new List<object[]>();
// Initialize current pick; will always be the leftmost consecutive x where x is subset size
ulong[] currentPick = new ulong[subsetSize];
for (ulong i = 0; i < subsetSize; i++) {
currentPick[i] = i;
}
while (true) {
// Add this subset's values to list of all subsets based on current pick
object[] subset = new object[subsetSize];
for (ulong i = 0; i < subsetSize; i++) {
subset[i] = fullSet[currentPick[i]];
}
allSubsets.Add(subset);
if (currentPick[0] + subsetSize >= (ulong)fullSet.LongLength) {
// Last pick must have been the final 3; end of subset generation
break;
}
// Update current pick for next subset
ulong shiftAfter = (ulong)currentPick.LongLength - 1;
bool loop;
do {
loop = false;
// Move current picker right
currentPick[shiftAfter]++;
// If we've gotten to the end of the full set, move left one picker
if (currentPick[shiftAfter] > (ulong)fullSet.LongLength - (subsetSize - shiftAfter)) {
if (shiftAfter > 0) {
shiftAfter--;
loop = true;
}
}
else {
// Update pickers to be consecutive
for (ulong i = shiftAfter+1; i < (ulong)currentPick.LongLength; i++) {
currentPick[i] = currentPick[i-1] + 1;
}
}
} while (loop);
}
return allSubsets;
}
This one is not from me, but it does the job!
List <ClassicCard> lowCardsToRemove = FrenchTarotUtil.checkCountLowCardForDiscardChien(handCards);
var result = Combinator.Combinations(lowCardsToRemove, 6);
public static class Combinator
{
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] { e }).Concat(c)));
}
}

Program to find minimum number in string

I have a c# class like so
internal class QueuedMinimumNumberFinder : ConcurrentQueue<int>
{
private readonly string _minString;
public QueuedMinimumNumberFinder(string number, int takeOutAmount)
{
if (number.Length < takeOutAmount)
{
throw new Exception("Error *");
}
var queueIndex = 0;
var queueAmount = number.Length - takeOutAmount;
var numQueue = new ConcurrentQueue<int>(number.ToCharArray().Where(m => (int) Char.GetNumericValue(m) != 0).Select(m=>(int)Char.GetNumericValue(m)).OrderBy(m=>m));
var zeroes = number.Length - numQueue.Count;
while (queueIndex < queueAmount)
{
int next;
if (queueIndex == 0)
{
numQueue.TryDequeue(out next);
Enqueue(next);
} else
{
if (zeroes > 0)
{
Enqueue(0);
zeroes--;
} else
{
numQueue.TryDequeue(out next);
Enqueue(next);
}
}
queueIndex++;
}
var builder = new StringBuilder();
while (Count > 0)
{
int next = 0;
TryDequeue(out next);
builder.Append(next.ToString());
}
_minString = builder.ToString();
}
public override string ToString() { return _minString; }
}
The point of the program is to find the minimum possible integer that can be made by taking out any x amount of characters from a string(example 100023 is string, if you take out any 3 letters, the minimum int created would be 100). My question is, is this the correct way to do this? Is there a better data structure that can be used for this problem?
First Edit:
Here's how it looks now
internal class QueuedMinimumNumberFinder
{
private readonly string _minString;
public QueuedMinimumNumberFinder(string number, int takeOutAmount)
{
var queue = new Queue<int>();
if (number.Length < takeOutAmount)
{
throw new Exception("Error *");
}
var queueIndex = 0;
var queueAmount = number.Length - takeOutAmount;
var numQueue = new List<int>(number.Where(m=>(int)Char.GetNumericValue(m)!=0).Select(m=>(int)Char.GetNumericValue(m))).ToList();
var zeroes = number.Length - numQueue.Count;
while (queueIndex < queueAmount)
{
if (queueIndex == 0)
{
var nextMin = numQueue.Min();
numQueue.Remove(nextMin);
queue.Enqueue(nextMin);
} else
{
if (zeroes > 1)
{
queue.Enqueue(0);
zeroes--;
} else
{
var nextMin = numQueue.Min();
numQueue.Remove(nextMin);
queue.Enqueue(nextMin);
}
}
queueIndex++;
}
var builder = new StringBuilder();
while (queue.Count > 0)
{
builder.Append(queue.Dequeue().ToString());
}
_minString = builder.ToString();
}
public override string ToString() { return _minString; }
}
A pretty simple and efficient implementation can be made, once you realize that your input string digits map to the domain of only 10 possible values: '0' .. '9'.
This can be encoded as the number of occurrences of a specific digit in your input string using a simple array of 10 integers: var digit_count = new int[10];
#MasterGillBates describes this idea in his answer.
You can then regard this array as your priority queue from which you can dequeue the characters you need by iteratively removing the lowest available character (decreasing its occurrence count in the array).
The code sample below provides an example implementation for this idea.
public static class MinNumberSolver
{
public static string GetMinString(string number, int takeOutAmount)
{
// "Add" the string by simply counting digit occurrance frequency.
var digit_count = new int[10];
foreach (var c in number)
if (char.IsDigit(c))
digit_count[c - '0']++;
// Now remove them one by one in lowest to highest order.
// For the first character we skip any potential leading 0s
var selected = new char[takeOutAmount];
var start_index = 1;
selected[0] = TakeLowest(digit_count, ref start_index);
// For the rest we start in digit order at '0' first.
start_index = 0;
for (var i = 0; i < takeOutAmount - 1; i++)
selected[1 + i] = TakeLowest(digit_count, ref start_index);
// And return the result.
return new string(selected);
}
private static char TakeLowest(int[] digit_count, ref int start_index)
{
for (var i = start_index; i < digit_count.Length; i++)
{
if (digit_count[i] > 0)
{
start_index = ((--digit_count[i] > 0) ? i : i + 1);
return (char)('0' + i);
}
}
throw new InvalidDataException("Input string does not have sufficient digits");
}
}
Just keep a count of how many times each digit appears. An array of size 10 will do. Count[i] gives the count of digit i.
Then pick the smallest non-zero i first, then pick the smallest etc and form your number.
Here's my solution using LINQ:
public string MinimumNumberFinder(string number, int takeOutAmount)
{
var ordered = number.OrderBy(n => n);
var nonZero = ordered.SkipWhile(n => n == '0');
var zero = ordered.TakeWhile(n => n == '0');
var result = nonZero.Take(1)
.Concat(zero)
.Concat(nonZero.Skip(1))
.Take(number.Length - takeOutAmount);
return new string(result.ToArray());
}
You could place every integer into a list and find all possible sequences of these values. From the list of sequences, you could sort through taking only the sets which have the number of integers you want. From there, you can write a quick function which parses a sequence into an integer. Next, you could store all of your parsed sequences into an array or an other data structure and sort based on value, which will allow you to select the minimum number from the data structure. There may be simpler ways to do this, but this will definitely work and gives you options as far as how many digits you want your number to have.
If I'm understanding this correctly, why don't you just pick out your numbers starting with the smallest number greater than zero. Then pick out all zeroes, then any remaining number if all the zeroes are picked up. This is all depending on the length of your ending result
In your example you have a 6 digit number and you want to pick out 3 digits. This means you'll only have 3 digits left. If it was a 10 digit number, then you would end up with a 7 digit number, etc...
So have an algorithm that knows the length of your starting number, how many digits you plan on removing, and the length of your ending number. Then just pick out the numbers.
This is just quick and dirty code:
string startingNumber = "9999903040404"; // "100023";
int numberOfCharactersToRemove = 3;
string endingNumber = string.Empty;
int endingNumberLength = startingNumber.Length - numberOfCharactersToRemove;
while (endingNumber.Length < endingNumberLength)
{
if (string.IsNullOrEmpty(endingNumber))
{
// Find the smallest digit in the starting number
for (int i = 1; i <= 9; i++)
{
if (startingNumber.Contains(i.ToString()))
{
endingNumber += i.ToString();
startingNumber = startingNumber.Remove(startingNumber.IndexOf(i.ToString()), 1);
break;
}
}
}
else if (startingNumber.Contains("0"))
{
// Add any zeroes
endingNumber += "0";
startingNumber = startingNumber.Remove(startingNumber.IndexOf("0"), 1);
}
else
{
// Add any remaining numbers from least to greatest
for (int i = 1; i <= 9; i++)
{
if (startingNumber.Contains(i.ToString()))
{
endingNumber += i.ToString();
startingNumber = startingNumber.Remove(startingNumber.IndexOf(i.ToString()), 1);
break;
}
}
}
}
Console.WriteLine(endingNumber);
100023 starting number resulted in 100 being the end result
9999903040404 starting number resulted in 3000044499 being the end result
Here's my version to fix this problem:
DESIGN:
You can sort your list using a binary tree , there are a lot of
implementations , I picked this one
Then you can keep track of the number of the Zeros you have in your
string Finally you will end up with two lists: I named one
SortedDigitsList and the other one ZeroDigitsList
perform a switch case to determine which last 3 digits should be
returned
Here's the complete code:
class MainProgram2
{
static void Main()
{
Tree theTree = new Tree();
Console.WriteLine("Please Enter the string you want to process:");
string input = Console.ReadLine();
foreach (char c in input)
{
// Check if it's a digit or not
if (c >= '0' && c <= '9')
{
theTree.Insert((int)Char.GetNumericValue(c));
}
}
//End of for each (char c in input)
Console.WriteLine("Inorder traversal resulting Tree Sort without the zeros");
theTree.Inorder(theTree.ReturnRoot());
Console.WriteLine(" ");
//Format the output depending on how many zeros you have
Console.WriteLine("The final 3 digits are");
switch (theTree.ZeroDigitsList.Count)
{
case 0:
{
Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], theTree.SortedDigitsList[1], theTree.SortedDigitsList[2]);
break;
}
case 1:
{
Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], 0, theTree.SortedDigitsList[2]);
break;
}
default:
{
Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], 0, 0);
break;
}
}
Console.ReadLine();
}
}//End of main()
}
class Node
{
public int item;
public Node leftChild;
public Node rightChild;
public void displayNode()
{
Console.Write("[");
Console.Write(item);
Console.Write("]");
}
}
class Tree
{
public List<int> SortedDigitsList { get; set; }
public List<int> ZeroDigitsList { get; set; }
public Node root;
public Tree()
{
root = null;
SortedDigitsList = new List<int>();
ZeroDigitsList = new List<int>();
}
public Node ReturnRoot()
{
return root;
}
public void Insert(int id)
{
Node newNode = new Node();
newNode.item = id;
if (root == null)
root = newNode;
else
{
Node current = root;
Node parent;
while (true)
{
parent = current;
if (id < current.item)
{
current = current.leftChild;
if (current == null)
{
parent.leftChild = newNode;
return;
}
}
else
{
current = current.rightChild;
if (current == null)
{
parent.rightChild = newNode;
return;
}
}
}
}
}
//public void Preorder(Node Root)
//{
// if (Root != null)
// {
// Console.Write(Root.item + " ");
// Preorder(Root.leftChild);
// Preorder(Root.rightChild);
// }
//}
public void Inorder(Node Root)
{
if (Root != null)
{
Inorder(Root.leftChild);
if (Root.item > 0)
{
SortedDigitsList.Add(Root.item);
Console.Write(Root.item + " ");
}
else
{
ZeroDigitsList.Add(Root.item);
}
Inorder(Root.rightChild);
}
}

Categories