Make something have a 1 percent chance of happening in Unity/C# - c#

I want a rare object in my game to spawn 1 out of 100 times. This is my code:
int random = Random.Range(0, 99);
if (random < 1)
{
//rare gameobject spawns
}
else
{
//common gameobject spawns
}
This makes sense to me but it doesn´t seem to work correctly. I have seen this rare gameobject about 1 in 20-30 times. Why does it happen so often?

The problem with that is that making things really random is quite a diffucult task for a computer. There is lot of literature on generating random and pseudo-random values and I'm not an expert in this subject, but the basic idea consists of taking an occurring and continually changing value on your system (e.g. the time) and modify it in order to receive a "random" value.
When you won't receive values that fit your purpose (in your case, the event occurs too often), try to use another (equals more complex) algorithm.
Edit: Test your function isolated by surrounding it with some kind of test function like this:
using UnityEngine;
public class RandomTest : MonoBehaviour
{
private const int TestRuns = 1000000;
private const int Probability = 1;
// Use this for initialization
void Start ()
{
int hits = 0;
for (int i = 0; i < TestRuns; i++)
{
int randomValue = Random.Range(0, 100);
if (randomValue < Probability)
{
hits++;
}
}
float probability = (float) hits / TestRuns * 100;
Debug.Log("Outcome: " + probability.ToString("f4") + " %");
}
}
For me, the values are quite close to what is expected and should (didn't calculate it) lay within the standard distribution:
Outcome: 0.9984 %

Related

Get random float with % odds (Gambling Crash game style)

I am trying to figure out how to create a random float with custom odds.
The game i am trying to replicate is Crash. So the minimum number would be 1, and the max could be for example 1000.
I have the formula from this site https://bitcointalk.org/index.php?topic=5265896.0
CRASH MULTIPLIER = [(E*100 - H)/(E-H)]/100
Also there is some .js code here from getting the number https://roobet.com/fair but it has hashes and stuff (i dont have them, and i dont need them for this project)
And found this thread too: Math behind generating random number (Crash game BTC Casino)
Anyone know how to do this in C#?
Also should i be using System.Random in this? is it actually "random"?
I know how to set the odds, it would go something like this:
Random random = new Random();
double Number = random.NextDouble() * (100 - 1) + 1;
if (Number == 50) //would give me 1% odds
{
CrashRandomNumber = 1; // = means that the Crash game would crash instantly upon starting
}
In C# try implementing it using this algorithm. It worked for me, hope it does for you
using System;
public class myClass
{
public static void Main(string[] args)
{
Console.Write("Please enter a value for E :" );
double E = Convert.ToDouble(Console.Read());
Random rnd = new Random();
double H = rnd.Next(1, Convert.ToInt32(E+1));
double crash = (E*100 - H)/(100*(E-H));
Console.WriteLine($" The value of crash is : {crash}");
}
}

Doubling Game Simulation

I would like to know if this simulator works as it should because I don't think these are logical answers, but can't capture mistake either.
I have written a simulator for the following game(Given a deck of cards and 1 point) to find most optimal strategy(what is dealers highest card to continue game)
1. Dealer picks a card and shows it to you(Dealer can't pick Joker)
2. You decide whether to play or no
3.1. If you don't play you get current points and finish game
3.2. If you play you pick a Card
3.2.1. If your card is higher you get double points and go back to step 1
3.2.2. If your and dealer's cards are equal you go back to step 1
3.2.3. If dealer's card is higher you lose all points and finish
The simulation shows win coefficient for choosing each MAX card to play.It shows these numbers which is highly doubtful to me.I expected it to grow to 1.5 till 7 and then go back to 1.
(First-Win/number of simulations,Second-Max card dealer can get for you to continue game)
1 -1
1.0853817 0
1.1872532 1
1.3126581 2
1.4672619 3
1.6704736 4
1.9485809 5
2.2674231 6
2.9993735 7
3.5692085 8
4.3581477 9
4.0109722 10
2.3629856 11
0 12
Here's C# code:
using System;
namespace Codeforces
{
class Program
{
static int[] k = new int[54];
static Random rand = new Random();
static long Doubling(int i, long f)
{
int d = rand.Next(52);
if (k[d] > i) return f;
int ch = d;
while (ch == d) ch = rand.Next(54);
if (k[d] > k[ch]) return 0;
if (k[d] == k[ch]) return Doubling(i, f);
return Doubling(i, f * 2);
}
static void Main(string[] args)
{
for (int i = 0; i < 54; i++) k[i] = i / 4;
for (int i = -1; i < 13; i++)
{
long sum = 0;
for (int j = 0; j < 1e7; j++)
{
sum += Doubling(i, 1);
}
Console.WriteLine(sum / 1.0e7 + " " + i);
}
}
}
}
I'm not a C# programmer, but it looks like your basic approach is mostly correct. I would recommend using a loop rather than recursion.
Your problem description is vague regarding the value of jokers and whether dealer discards jokers when drawn or magically just doesn't draw them—you seem to have gone for the latter if I'm reading your code correctly.
It also appears that the way you implemented the recursion implicitly replaces cards in the deck after each play of the game rather than playing through the deck.
When I implemented this independently in another language, I got comparable results. Looks to me like your intuition is wrong.

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().

How do I loop a function in c#?

I have been given this question, in my computer science class, and I cannot figure out how to answer it.
Question:
•Create a procedure called tossCoins with one integer parameter - numTosses
•Inside your tossCoins procedure, call your tossCoin function from challenge #5
numTosses times. So for example if we enter tossCoins(50), the tossCoin function will be called 50 times.
•Your tossCoins procedures should then print out the number of times that the coin landed on heads and the number of times the coin landed on tails
•Extend your main program, so that the user can choose how many times to toss the coin
I have already created the tossCoin function, but cannot figure out how to run it the amount of times the user asks, or how to create a tally of heads and tails.
This is the code I have so far:
static void Main(string[] args)
{
Random rnd = new Random();
Console.WriteLine(tossCoin(rnd));
Console.Write("Please enter a number: ");
int numTosses = Convert.ToInt16(Console.ReadLine());
Console.ReadLine();
}
static string tossCoin(Random rnd)
{
int num = rnd.Next(1, 3);
string heads = "Heads";
string tails = "Tails";
if(num == 1)
{
return heads;
}
else
{
return tails;
}
}
static void tossCoins(int numTosses)
{
int headsTally = 0;
int tailsTally = 0;
for(int i = 0; i < numTosses; i++)
{
string outcome = tossCoin(rnd);
numTosses--;
if(outcome == heads)
{
headsTally++;
}
else
{
tailsTally++;
}
}
}
}
If anyone can help, it would be great, as I am trying to learn the different rules of using functions, and procedures. Thanks, Leighton.
A couple things, first it looks like you've almost got it. You just need to call your tossCoins() function in your main method. Second, you don't need to decrement numTosses in your for loop, ie. remove the line numTosses--;
EDIT: as Tommy said, the statement outcome == heads wont compile since heads isn't defined. Either replace heads with "Heads" or define it earlier in the function. And yea you might want to return/print the result.

Why am I getting strange results when generating Random() numbers?

My program needs to:
a. Generate an array of 20 random integers from zero to nine. Search for the first occurrence, if any, of the number 7, and report its position in the array.
b. Repeat the computation of part a 1000 times, and for each position in the array, report the number of times that the first occurrence of a 7 in the array is at that position
However whenever I run the program I get strange results (different every time) such as:
No sevens found at any position
1000 sevens found at one position and no sevens found anywhere else
Hundreds of sevens found in 2 positions, and none found anywhere else.
Does anyone have an idea what is wrong with my program?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Week_6_Project_2
{
class Program
{
static int intArrayLength = 20;
static int[] resultsArray = new int[intArrayLength];
public static Array generateRandomArray() {
int[] randomNumberArray = new int[intArrayLength];
Random random = new Random();
int popcounter = 0;
while (popcounter < intArrayLength) {
randomNumberArray[popcounter] = random.Next(0, 10);
popcounter += 1;
}
return randomNumberArray;
}
public static void searchForSevens()
{
int counter = 0;
int[] randomArray = (int[])generateRandomArray();
while (counter < intArrayLength)
{
if (randomArray[counter] == 7)
{
resultsArray[counter] += 1;
counter = intArrayLength;
}
counter += 1;
}
}
static void Main()
{
int searchCounter = 0;
while (searchCounter < 1000)
{
searchForSevens();
searchCounter += 1;
}
int displayCounter = 0;
while (displayCounter < intArrayLength)
{
Console.WriteLine("Number of first occurrence of 7 at position {0} = {1}", displayCounter, resultsArray[displayCounter]);
displayCounter += 1;
}
Console.ReadLine();
}
}
}
Your main problem is that each searchForSevens() test only takes a small fraction of time and the Random class uses auto-seeding from the clock. The clock however has a limited resolution. The result is that many (sometimes all) of your random sequences will be the same. And at most you will have 2 or 3 different result sets.
The simple fix for this single-threaded program is to use 1 static instance of Random.
You're instantiating a new instance of Random every time you call the generateRandomArray method. Since the random number generator uses the current time as a seed, instantiating two Random instances at the same time results in the same numbers being generated, which explains your unexpected results.
To solve your problem, you should only instantiate one Random instance, store it in private field, and reuse it every time you call the Next method.
The problem I assume stems from the fact, that Random() uses current time as seed. And the computation happens so fast, that each time new Random() is called, it uses same time. So you get same sequence of numbers.
To fix this, you simply need to set seed by yourself, incremmenting it every cycle should be enough.
long base = DateTime.Now.TotalMilliseconds;
Random rand = new Random(base+searchCounter);
.. something like that.
I will not answer but will try to give an analogy for the people that think that they need more then one Random instance...
Suppose that you need to fill 6 sheets of paper with random numbers from 1-6. Ask yourself this: do you need 6 dices or one to do the job? If you answer that you need more than one dice, ask yourself this: how different or more random is throwing different dice instead of same dice every time?
I mean, if you throw ONE on a dice, next throw of a dice won't have any less chance to be ONE again than it would be any other number. It goes against intuition, but it is mathematically and statistically so.
In your original code, you're calling the Random method in rapid succession [broken example based on OP's original code], thus seeding the method with the same number, resulting in duplicate "random" numbers. Creating a static member will ensure randomness simply because you're only creating a single instance of it.
Try creating a single static instance of random like this. [static member example].
static readonly Random Random = new Random();
Based on this, here's how I would solve your particular problem.
using System;
namespace Week_6_Project_2
{
class Program
{
// ******************************************
// THIS IS A SINGLE INSTANCE OF Random.
// read below as to why I'm seeding the instantiation of Random();
static readonly Random Random = new Random(Guid.NewGuid().GetHashCode());
// ******************************************
private const int IntArrayLength = 20;
static readonly int[] ResultsArray = new int[IntArrayLength];
public static Array GenerateRandomArray()
{
var randomNumberArray = new int[IntArrayLength];
var popcounter = 0;
while (popcounter < IntArrayLength)
{
randomNumberArray[popcounter] = Random.Next(0, 10);
popcounter += 1;
}
return randomNumberArray;
}
public static void SearchForSevens()
{
var counter = 0;
var randomArray = (int[])GenerateRandomArray();
while (counter < IntArrayLength)
{
if (randomArray[counter] == 7)
{
ResultsArray[counter] += 1;
counter = IntArrayLength;
}
counter += 1;
}
}
static void Main()
{
var searchCounter = 0;
while (searchCounter < 1000)
{
SearchForSevens();
searchCounter += 1;
}
var displayCounter = 0;
while (displayCounter < IntArrayLength)
{
Console.WriteLine("Number of first occurrence of 7 at position {0} = {1}", displayCounter, ResultsArray[displayCounter]);
displayCounter += 1;
}
Console.ReadLine();
}
}
}
Further reading about Random()
Beyond the answer above, sometimes it is necessary to seed Random(int); (I like to use the HashCode of a Guid) to ensure further randomness. This is because the default seed uses the clock which from the docs [microsoft] has a finite resolution. If your class is instantiated multiple times in quick succession (< 16ms), you will get the same seed from the clock... this breaks stuff.
[example of seeded Random(int) run in rapid succession with random results]
"using the parameterless constructor to create different Random objects in close succession creates random number generators that produce identical sequences of random numbers."
hear me when I say that you should NOT instantiate a new Random on every iteration of a loop -- use a static member
Another valid reason to implement your own seed is when you want to recreate a random sequence [example of two identical lists based on same seed]. Re-using the same seed will recreate the sequence since the sequence is based on the seed.
note: others might say that seeding it is not necessary [link], but I personally believe that for the additional few keystrokes and the microscopic hit on the clock, you might as well increase the probability of a unique seed. It doesn't hurt anything, and in some situations it can help.

Categories