method for throwing dice, Yatzee game - c#

I'm creating a simple game (Yatzee) in C#. I have created an array which holds 5 different dice
int[] dice = new int[5];
Now, I want to create a method that throw one of these five dices. Which die that should be thrown, should be passed in as an argument in that method. So this is how I tried:
public void throwDice(int x)
{
Random r1 = new Random(6);
r1.x;
}
What I believe is happening, is that the method takes in an argument x, that randomly should throw the dice to be a number between 1-6. But I'm getting error with when I write saying : r1.x;
So, why I'm asking here, is if I could get some guidance. Am I on the right track here, or am I totally lost?

You are using the Random object wrong. The constructor parameter is a seed. You need r1.Next(6)+1.
See the related post for details: How do I generate a random int number in C#?.
What you probably want to do is this:
Random rnd = new Random();
int[] dice = new int[5];
void ThrowDie(int x)
{
dice[x] = rnd.Next(6)+1;
}

r1 is an instance of a Random class. x is a parameter of your throwDice method.
That does not mean your r1 must have a field called x.
I think you are looking for something like;
Random r1 = new Random();
public int throwDice()
{
return r1.Next(1, 6);
}
Rememer, in Random.Next method lowerbound is inclusive but upperbound is exclusive.

Here is another approach to this matter:
public class RandomDice
{
private const int NUMBER_OF_DICE = 5;
private const int MAX_DICE_VALUE = 6;
private static readonly Random Rng = new Random();
public List<int> NumberList = new List<int>();
private static IEnumerable<int> GetRandomNumbers()
{
while (true)
yield return Rng.Next(1, MAX_DICE_VALUE + 1);
}
internal void AddDice()
{
NumberList.Clear();
NumberList.AddRange(GetRandomNumbers().Take(NUMBER_OF_DICE).ToList());
}
}

Related

Cannot call Random method

I'm trying to create a List<int> and randomize the order based on a user inputted seed but nothing is written to the list. I'm assuming because the Randomize method is never called but could also be the List is never being written to but breakpoints suggest it's never accessed.
Main file :
public static List<int> TimeskipOrder = new(17);
public static int Seed = 12345;
public static void Shuffle<T>(List<T> list, int seed)
{
Seed = seed;
var rng = new Random(seed);
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;
}
}
public static void Randomize()
{
var numbers = new List<int>(Enumerable.Range(1, 17));
Shuffle(numbers, Seed);
TimeskipOrder.Clear();
numbers.AddRange(TimeskipOrder);
}
Form :
public void button2_Click(object sender, EventArgs e)
{
TimeSkip.Randomize();
}
When List.ElementAt is checked it returns this error :
Index was out of range. Must be non-negative and less than the size of
the collection. Parameter name: index'
Apart from anything else, this:
numbers.AddRange(TimeskipOrder);
should almost certainly be this:
TimeskipOrder.AddRange(numbers);
Given that you clear TimeskipOrder immediately before that, what could be the point of adding the items from that empty list to another list that you will discard immediately after? This is an example of why you need to consider the logic first, then ensure that the code you write implements that logic. If you had checked the code against the required logic, you'd have seen that the two did not coincide.

Unable to cast object of type 'Dice' to type 'System.IConvertible'

I'm trying to do a dice game but for some reason this error occurs.
Unable to cast object of type 'Dice' to type 'System.IConvertible'
Here is my code:
class Dice
{
int result;
public void DiceRoll()
{
Random rnd = new Random();
result = rnd.Next(1, 7);
}
}
Console.WriteLine("Player 1 Turn" + roll);
int enterscore1 = Convert.ToInt32(roll);
Console.WriteLine("Player 2 Turn" + roll);
int enterscore2 = Convert.ToInt32(roll);
Some notes:
You'd often use one of Convert.ToInt32, int.TryParse, or int.TryParse when getting input from a user (i.e. as text) and want to get its numeric value (as an int value).
Here you have the bones of a Dice class which works with an int already, so there is no need for conversion.
I would strongly recommend you don't try to implement IConvertible like the message implies - that's not really what it's designed for.
Note that for cases like yours, it's strongly recommended you have a single instance of a Random because the constructor is
using a time-dependent default seed value.
so making multiple Random instances in quick succession are liable to use the same seed. If you do this, because methods on the Random class are not threadsafe, you should lock on some object to prevent multiple threads from calling its methods at the same time (as I do below).
There are several ways to do what you want. Here are a few:
Have a single instance of a Dice and roll it many times:
public class Dice
{
private static readonly Random random = new Random();
public int Result
{
get;
private set;
}
public void Roll()
{
lock ( random )
Result = random.Next(1, 7);
}
}
public static void Main()
{
var dice = new Dice();
dice.Roll();
var player1Result = dice.Result;
Console.WriteLine("Player 1 rolls: " + player1Result);
dice.Roll();
var player2Result = dice.Result;
Console.WriteLine("Player 2 rolls: " + player2Result);
}
(try it here)
Notice that I've assigned a variable for each roll - this enables you to compare the results using > and < to find out who wins.
Have a separate Dice instance for each player:
public class Dice
{
private static readonly Random random = new Random();
private readonly int _minValue;
private readonly int _maxValue;
public Dice(int minValue, int maxValue)
{
_minValue = minValue;
_maxValue = maxValue;
}
public int Result
{
get;
private set;
}
public void Roll()
{
lock ( random )
Result = random.Next(_minValue, _maxValue + 1);
}
}
public static void Main()
{
var player1Dice = new Dice(1, 6);
player1Dice.Roll();
var player1Result = player1Dice.Result;
Console.WriteLine("Player 1 rolls: " + player1Result);
var player2Dice = new Dice(1, 5);
player2Dice.Roll();
var player2Result = player2Dice.Result;
Console.WriteLine("Player 2 rolls: " + player2Result);
}
(try it here)
You can still do comparisons, but because you have different Dice instances, you could add your own constructor to Dice to, for instance, stack the odds in favour of one player (here I've made the second player's dice only ever roll 1-5).
Just have a static Dice class
public static class Dice
{
private static readonly Random random = new Random();
public static int Roll()
{
lock ( random )
return random.Next(1, 7);
}
}
public static void Main()
{
var player1Result = Dice.Roll();
Console.WriteLine("Player 1 rolls: " + player1Result);
var player2Result = Dice.Roll();
Console.WriteLine("Player 2 rolls: " + player2Result);
}
(try it here)
If you're never going to do anything fancy with the Dice and it isn't expected to have any state (e.g. roll different values, or know about previous rolls) and you need to have a separate class, this is how I'd do it. You don't need a constructor because Dice is static. Again, you can do comparisons with the two results.
If you declared roll as a variable of Dice class, your code should be
int enterscore1 = Convert.ToInt32(roll.result);
Console.WriteLine("Player 2 Turn" + roll);
int enterscore2 = Convert.ToInt32(roll.result);
Unable to cast object of type 'Dice' to type 'System.IConvertible'
This error means you are putting argument that has no IConvertible interface which is essential to Convert class methods.
you need to include int enterscore1 = DiceRoll(); and it would help to define the random variable as an integer within the dice roll method

Generate 10 unique integers in C# for Unity

I want to generate 10 'random' numbers, but they have to be unique. I have tried something, but is there someone who can help me out with something better?
My code:
List<int> ran = new List<int>();
Random rnd = new Random();
public static int randomValue;
int tempRandom;
public int randomNum()
{
if(ran.Count == 0)
{
ran.Add(0);
ran.Add(1);
ran.Add(2);
ran.Add(3);
ran.Add(4);
ran.Add(5);
ran.Add(6);
ran.Add(7);
}
tempRandom = rnd.Next(0, ran.Count);
randomValue = ran[randomValue];
ran.RemoveAt(tempRandom);
return randomValue;
}
Is this what you're trying to say? If not, please specify how you mean further. This code should give you a number between 1-10 that hasn't been already used. This code will only work 10 times.
Random rnd = new Random();
List<int> usedNumbers = new List<int>();
public int RandomNum(){
int number;
do {
number = rnd.Next(1, 10);
} while(usedNumbers.IndexOf(number) == -1);
usedNumbers.Add(number);
return number;
}
Straight answer to your question (not regarding if you actually want what you are asking for):
Random.Range( int.MinValue, int.MaxValue );
This simply produces a random int in the range of all integers. For 10 numbers, the probability of duplicates is so little that every number will be unique.

c# dice roll (i <= maxRolls)

I have two issues that I'm not sure how to fix.
diceThrow() is supposed to randomly roll a die and come up with an answer 1-6 multiple times, but only comes up with one 1-6 answer and only does that. i.e. (6, 6, 6, 6, 6, 6, etc)
and for rollDice(), I'm not sure if I just poorly defined "i" or maxRolls, but it should be that when i > maxRolls, the program should end and reset.
Any advice on how to fix either of these is greatly appreciated, thanks!
//somewhere else in code
int maxRolls = RollsNumber();
int throwresult = diceThrow();
int i;
//*******************************
private void rollButton_Click(object sender, EventArgs e)
{
rollDice();
wagerTextBox.Text = null;
wagerTextBox.Text = scoreTextBox.Text;
diceThrow();
MessageBox.Show(Convert.ToString(throwresult));
if (maxRolls < i)
{
MessageBox.Show("You got too greedy.");
//reset the form
}
}
// Decides the maximum number of rolls before the player loses
static public int RollsNumber()
{
Random rolls = new Random();
return rolls.Next(1, 10);
}
// Throws the dice
static public int diceThrow()
{
Random dice = new Random();
return dice.Next(1, 7);
}
private void rollDice()
{
for (i = 0; i <= maxRolls; i++)
{
int wager = Convert.ToInt32(wagerTextBox.Text);
int score = wager * 100;
scoreTextBox.Text = Convert.ToString(score);
}
}
}
}
You are using same seed with the Random.
As msdn states in Random class
The random number generation starts from a seed value. If the same seed is used repeatedly, the same series of numbers is generated. One way to produce different sequences is to make the seed value time-dependent, thereby producing a different series with each new instance of Random.
A simple way in your case is to not create new Random each time.
// Throws the dice
static Random diceRandom = new Random();
static public int diceThrow()
{
return diceRandom .Next(1, 7);
}

How can I get true randomness in this class without Thread.Sleep(300)?

I've made a class (code below) that handles the creation of a "matching" quiz item on a test, this is the output:
It works fine.
However, in order to get it completely random, I have to put the thread to sleep for at least 300 counts between the random shuffling of the two columns, anything lower than 300 returns both columns sorted in the same order, as if it is using the same seed for randomness:
LeftDisplayIndexes.Shuffle();
Thread.Sleep(300);
RightDisplayIndexes.Shuffle();
What do I have to do to make the shuffling of the two columns completely random without this time wait?
full code:
using System.Collections.Generic;
using System;
using System.Threading;
namespace TestSort727272
{
class Program
{
static void Main(string[] args)
{
MatchingItems matchingItems = new MatchingItems();
matchingItems.Add("one", "111");
matchingItems.Add("two", "222");
matchingItems.Add("three", "333");
matchingItems.Add("four", "444");
matchingItems.Setup();
matchingItems.DisplayTest();
matchingItems.DisplayAnswers();
Console.ReadLine();
}
}
public class MatchingItems
{
public List<MatchingItem> Collection { get; set; }
public List<int> LeftDisplayIndexes { get; set; }
public List<int> RightDisplayIndexes { get; set; }
private char[] _numbers = { '1', '2', '3', '4', '5', '6', '7', '8' };
private char[] _letters = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
public MatchingItems()
{
Collection = new List<MatchingItem>();
LeftDisplayIndexes = new List<int>();
RightDisplayIndexes = new List<int>();
}
public void Add(string leftText, string rightText)
{
MatchingItem matchingItem = new MatchingItem(leftText, rightText);
Collection.Add(matchingItem);
LeftDisplayIndexes.Add(Collection.Count - 1);
RightDisplayIndexes.Add(Collection.Count - 1);
}
public void DisplayTest()
{
Console.WriteLine("");
Console.WriteLine("--TEST:-------------------------");
for (int i = 0; i < Collection.Count; i++)
{
int leftIndex = LeftDisplayIndexes[i];
int rightIndex = RightDisplayIndexes[i];
Console.WriteLine("{0}. {1,-12}{2}. {3}", _numbers[i], Collection[leftIndex].LeftText, _letters[i], Collection[rightIndex].RightText);
}
}
public void DisplayAnswers()
{
Console.WriteLine("");
Console.WriteLine("--ANSWERS:-------------------------");
for (int i = 0; i < Collection.Count; i++)
{
string leftLabel = _numbers[i].ToString();
int leftIndex = LeftDisplayIndexes[i];
int rightIndex = RightDisplayIndexes.IndexOf(leftIndex);
string answerLabel = _letters[rightIndex].ToString();
Console.WriteLine("{0}. {1}", leftLabel, answerLabel);
}
}
public void Setup()
{
do
{
LeftDisplayIndexes.Shuffle();
Thread.Sleep(300);
RightDisplayIndexes.Shuffle();
} while (SomeLinesAreMatched());
}
private bool SomeLinesAreMatched()
{
for (int i = 0; i < LeftDisplayIndexes.Count; i++)
{
int leftIndex = LeftDisplayIndexes[i];
int rightIndex = RightDisplayIndexes[i];
if (leftIndex == rightIndex)
return true;
}
return false;
}
public void DisplayAsAnswer(int numberedIndex)
{
Console.WriteLine("");
Console.WriteLine("--ANSWER TO {0}:-------------------------", _numbers[numberedIndex]);
for (int i = 0; i < Collection.Count; i++)
{
int leftIndex = LeftDisplayIndexes[i];
int rightIndex = RightDisplayIndexes[i];
Console.WriteLine("{0}. {1,-12}{2}. {3}", _numbers[i], Collection[leftIndex].LeftText, _letters[i], Collection[rightIndex].RightText);
}
}
}
public class MatchingItem
{
public string LeftText { get; set; }
public string RightText { get; set; }
public MatchingItem(string leftText, string rightText)
{
LeftText = leftText;
RightText = rightText;
}
}
public static class Helpers
{
public static void Shuffle<T>(this IList<T> list)
{
Random rng = new Random();
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;
}
}
}
}
Move Random rng = new Random(); to a static variable.
MSDN says "The default seed value is derived from the system clock and has finite resolution". When you create many Random objects within a small time range they all get the same seed and the first value will be equal to all Random objects.
By reusing the same Random object you will advance to the next random value from a given seed.
Only make one instance of the Random class. When you call it without a constructor it grabs a random seed from the computer clock, so you could get the same one twice.
public static class Helpers
{
static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
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;
}
}
}
I have to put the thread to sleep for
at least 300 counts between the random
shuffling of the two columns, anything
lower than 300 returns both columns
sorted in the same order, as if it is
using the same seed for randomness
You've answered your own question here. It is "as if it is using the same seed" because it is using the same seed! Due to the relatively coarse granularity of the Windows system clock, multiple Random instances constructed at nearly the same time will have the same seed value.
As Albin suggests, you should just have one Random object and use that. This way instead of a bunch of pseudorandom sequences that all start at the same seed and are therefore identical, your Shuffle method will be based on a single pseudorandom sequence.
Considering that you have it as an extension method, you may desire for it to be reusable. In this case, consider having an overload that accepts a Random and one that doesn't:
static void Shuffle<T>(this IList<T> list, Random random)
{
// Your code goes here.
}
static void Shuffle<T>(this IList<T> list)
{
list.Shuffle(new Random());
}
This allows the caller to provide a static Random object if he/she's going to be calling Shuffle many times consecutively; on the other hand, if it's just a one-time thing, Shuffle can take care of the Random instantiation itself.
One last thing I want to point out is that since the solution involves using a single shared Random object, you should be aware that the Random class is not thread-safe. If there's a chance you might be calling Shuffle from multiple threads concurrently, you'll need to lock your Next call (or: what I prefer to do is have a [ThreadStatic] Random object for each thread, each one seeded on a random value provided by a "core" Random -- but that's a bit more involved).
Otherwise you could end up with Next suddenly just retuning an endless sequence of zeroes.
The problem is that you are creating your Random objects too close to each other in time. When you do that, their internal pseudo-random generators are seeded with the same system time, and the sequence of numbers they produce will be identical.
The simplest solution is to reuse a single Random object, either by passing it as an argument to your shuffle algorithm or storing it as a member-variable of the class in which the shuffle is implemented.
The way random generators work, roughly, is that they have a seed from which the random values are derived. When you create a new Random object, this seed is set to be the current system time, in seconds or milliseconds.
Let's say when you create the first Random object, the seed is 10000. After calling it three times, the seeds were 20000, 40000, 80000, generating whatever numbers form the seeds (let's say 5, 6, 2). If you create a new Random object very quickly, the same seed will be used, 10000. So, if you call it three times, you'll get the same seeds, 20000, 40000, and 80000, and the same numbers from them.
However, if you re-use the same object, the latest seed was 80000, so instead you'll generate three new seeds, 160000, 320000, and 640000, which are very likely to give you new values.
That's why you have to use one random generator, without creating a new one every time.
Try to use Random() just one time. You'll get the idea.

Categories

Resources