Generate random number each time and don't include last number - c#

I have 4 colors. I want to make it so that the player can't be the same color 2 times in a row. When a player collides with an object, the RandomColor() is called. So this function is called many times during the game and sometimes the player does not change his color.
using UnityEngine;
public class ColorManager : MonoBehaviour {
public SpriteRenderer player;
public string playerColor;
public Color[] colors = new Color[4];
private void Awake()
{
RandomColor();
}
public void RandomColor()
{
int index = Random.Range(0, 4);
switch (index)
{
case 0:
player.color = colors[0]; //colors[0] is orange
playerColor = "orange";
break;
case 1:
player.color = colors[1]; //colors[1] is pink
playerColor = "pink";
break;
case 2:
player.color = colors[2]; //colors[2] is blue
playerColor = "blue";
break;
case 3:
player.color = colors[3]; //colors[3] is purple
playerColor = "purple";
break;
}
}
}
Tried using while loop, do while loop, but I'm obviously doing it wrong, since I receive the same color twice in a row sometimes. It would be great if anyone figures it out and explains how/why it works, because I spent a big chunk of time on the issue and I am very curious.

First, you need a function that can generate a random number with exclusion. Below is what I use for that:
int RandomWithExclusion(int min, int max, int exclusion)
{
int result = UnityEngine.Random.Range(min, max - 1);
return (result < exclusion) ? result : result + 1;
}
Each time you call it, you need to store the result in a global variable so that you will pass that to the exclusion parameter next time you call it again.
I modified the function so that you don't have to do that each time it is called. The new RandomWithExclusion function will do that for you.
int excludeLastRandNum;
bool firstRun = true;
int RandomWithExclusion(int min, int max)
{
int result;
//Don't exclude if this is first run.
if (firstRun)
{
//Generate normal random number
result = UnityEngine.Random.Range(min, max);
excludeLastRandNum = result;
firstRun = false;
return result;
}
//Not first run, exclude last random number with -1 on the max
result = UnityEngine.Random.Range(min, max - 1);
//Apply +1 to the result to cancel out that -1 depending on the if statement
result = (result < excludeLastRandNum) ? result : result + 1;
excludeLastRandNum = result;
return result;
}
Test:
void Update()
{
Debug.Log(RandomWithExclusion(0, 4));
}
The last number will never appear in the next function call.
For your specific solution, simply replace
int index = Random.Range(0, 4);
with
int index = RandomWithExclusion(0, 4);

What you need to do is add a judgement if the RandomColor returns a same color as prev one, just call it again, easy right?

Related

Get the biggest value of an array

I have a textbox to write the position of an array and a textbox to write the value of that position. Every time I want to add a value and a position I click the button btnStoreValue
I created a function (CompareTwoNumbers) for another exercise that compares two numbers and returns the biggest
Using that function and avoiding the use of comparison characters like > and < I'm supposed to get the biggest value of the array
public partial class Form1 : ExerciseArray
{
int[] numbers = new int[10];
private int CompareTwoNumbers(int i, int j)
{
if (i < j)
{
return j;
}
return i;
}
private void btnBiggestValue_Click(object sender, EventArgs e)
{
//int n=1;
int counter = 0;
int highestPosition = CompareTwoNumbers(0, 1);
for(int i=0; i<10; i++){
//int j = CompareTwoNumbers(numbers[i], numbers[i+1])
//n = CompareTwoNumbers(numbers[n], numbers[i+1]
counter = CompareTwoNumbers(highestPosition, i);
}
txtBiggestValuePosition.Text= n.ToString();
txtBiggestValue.Text=numbers[n].ToString();
}
I've tried multiple things, using multiple variables, I tried to write it on paper to try to understand things better and I'm stuck. I don't know how is it possible to find that value using the function I created on the previous exercise (assuming the function I created is correct)
So, the core part of your question is that you want to know how to find the biggest number in an array using your helper function CompareTwoNumbers and then figure out what the value and position of the biggest number is.
Based on my understanding above, you have the framework almost set up correctly.
First off, CompareTwoNumbers should be updated to return a bool. Doing this will let you conditionally update your variables holding the biggest number value and position.
private int CompareTwoNumbers(int i, int j)
{
if (i < j)
{
return true;
}
return false;
}
To know what the largest value in an (unsorted) array is, you will need to iterate through every value. While doing so, you need to keep track of the value and position of the biggest value, only updating it when a bigger value is found.
private void btnBiggestValue_Click(object sender, EventArgs e)
{
// Store the bigget number's index and value
// We start with the first index and corresponding
// value to give us a starting point.
int biggestNumberIndex = 0;
int biggestNumber = numbers[0];
// Iterate through the array of numbers to find
// the biggest number and its index
for(int i=0; i<10; i++)
{
// If the current number is larger than the
// currently stored biggest number...
if(CompareTwoNumbers(biggestNumber, numbers[i])
{
// ...then update the value and index with
// the new biggest number.
biggestNumber = number[i];
biggestNumberIndex = i;
}
}
// Finally, update the text fields with
// the correct biggest value and biggest
// value position.
txtBiggestValuePosition.Text= biggestNumberIndex.ToString();
txtBiggestValue.Text=numbers[biggestNumberIndex].ToString();
}
This uses a Tuple to give you both the max index and max value from the same method:
public (int, int) FindMaxValue(int[] items)
{
int maxValue = items[0];
int maxIndex = 0;
for(int i=1;i<items.Length;i++)
{
if (items[i] > maxValue)
{
maxValue = items[i];
maxIndex = i;
}
}
return (maxIndex, maxValue);
}

How to activate and deactivate GameObjects with a delay in Untiy

I am working on a game, that has a keypad puzzle. A specific key combination lights up one by one, which the player must repeat to solve that puzzle. I am going to let the player know what the combination is by activating and deactivating some GameObjects systematically, one by one. As it suggests, there is some time delay between the deactivation of one GameObject and the activation of another. The problem is, in my code, all the GameObjects activate simultaneously instead of one by one, after a delay.
Here is the code:
public string Generate(int length, float delay)
{
// Variables for logic
string combination = "";
int prev = -1; int current = 0;
int rnd = 0;
for (int i = 0; i < length; i++)
{
rnd = Random.Range(0, BUTTONS);
while (rnd == prev)
{
rnd = Random.Range(0, BUTTONS);
}
prev = current;
current = rnd;
combination += current.ToString();
// Activation and Deactivation
StartCoroutine(GenerateDelay(delay, current));
}
return combination;
}
IEnumerator GenerateDelay(float delay, int index)
{
ButtonClicks[index].SetActive(true);
yield return new WaitForSeconds(delay);
ButtonClicks[index].SetActive(false);
}
The loop counter specifies the length of the combination. I believe there is something wrong with the Coroutine I made? Since all the objects activate simultaneously.
Here is the result in the game as well:
We can see, only one button should turn green at a time, but all of them do in this case. Any solutions?
You start all your Coroutines parallel so things happen at the same time.
StartCoroutine does not delay the method which calls it (unless it is yielded as well.
You would need to run the entire loop within a Coroutine in order to delay it as well.
You could simply split up the creation of the combination and while you already return it you start the visualization in the background in parallel
public string Generate(int length, float delay, Action<string> onCombination)
{
// Variables for logic
var combination = List<int>();
var prev = -1;
for (var i = 0; i < length; i++)
{
int rnd;
do
{
rnd = Random.Range(0, BUTTONS);
}
while (rnd == prev);
prev = rnd;
combination.Add(rnd);
}
StartCorouine (ShowCombination(combination, delay));
return string.Join("", combination);
}
private IEnumerator ShowCombination (IEnumerable<int> combination, float delay)
{
foreach(var i in combination)
{
ButtonClicks[i].SetActive(true);
yield return new WaitForSeconds(delay);
ButtonClicks[i].SetActive(false);
}
}
something alot easier then a coroutine is invoking a function, basically create a function to activate/deactivate the wanted object and whenever you want to call it do:
Invoke("FUNCTIONNAME", TIME);
and it will run the function specified after the TIME.

How to prevent same object from being picked from array twice in a row

I am having a bit of a problem figuring out how to randomly get an object out of a list that wasn't picked on the last update of a script. When this randomly instantiated object is spawned and reaches a certain y value, it will set itself to inactive. So when this object is not active, it will go through an array and pick another object at random. However, I do not want to include the previous active object.
example: blue ball was first active. Moves on the Y axis. Becomes inactive. Next object spawn should have no chance of being a blue ball. Any ideas and help will be greatly appreciated.
My code is below
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ballGeneratorShooter : MonoBehaviour
{
private int ballSelector;
private TagNumber theTagNumber;
public ObjectPooler[] theObjectballPools;//In my inspector, I have 5 prefab gameObjects attached
List<ObjectPooler> changingBalls;
public bool changeBalls;
public GameObject currentBall;
public GameObject newBall;
// Use this for initialization
void Start()
{
changingBalls = new List<ObjectPooler>();
currentBall = newBall;
}
// Update is called once per frame
void Update()
{
if (newBall == null)
{
ballSelector = Random.Range(0, theObjectballPools.Length);
newBall = theObjectballPools[ballSelector].GetPooledObject();
newBall.transform.position = transform.position;
newBall.transform.rotation = transform.rotation;
newBall.SetActive(true);
}
if (newBall.activeInHierarchy == false)
{
if (changeBalls)
{
for (int i = 0; i < theObjectballPools.Length; i++)
{
if (theObjectballPools[i].GetPooledObject().GetComponent<TagNumber>().tag != currentBall.GetComponent<TagNumber>().tag)
{
changingBalls.Add(theObjectballPools[i]);
}
//changingballs.Add(theObjectballPools[i]);
}
ballSelector = Random.Range(0, changingBalls.Count);
newBall = theObjectballPools[ballSelector].GetPooledObject();
Debug.Log(changingBalls.Count);
newBall.transform.position = transform.position;
newBall.transform.rotation = transform.rotation;
newBall.SetActive(true);
currentBall = newBall;
changeBalls = false;
changingBalls.Clear();
}
}
}
}
You need to store the random number to variable (lastRandomNum) each time you generate a random number. Now, use the function below that can generate a random number with exclusion.
int RandomWithExclusion(int min, int max, int exclusion)
{
var result = UnityEngine.Random.Range(min, max - 1);
return (result < exclusion) ? result : result + 1;
}
You pass in 0 to min, then theObjectballPools.Length or changingBalls.Count to the max, then finally, lastRandomNum value to the exclusion parameter.
You also need a boolean variable to determine if this is the first run. If this is the first run, use the Unity's Random.Range function then set the firstRun to false. If it is not the firstRun, use the RandomWithExclusion function from this answer and pass in the lastRandomNum value to exclude it. Also, store the generated random number to the lastRandomNum variable to be used next frame.
Below is a simplified version of what I said above. You have to incorporate that to your code.
GameObject[] yourItem = null;
bool firstRun = true;
int lastRandomNum = 0;
void Update()
{
if (firstRun)
{
firstRun = false;
//First run, use Random.Range
lastRandomNum = UnityEngine.Random.Range(0, yourItem.Length);
}
else
{
//Not First run, use RandomWithExclusion
lastRandomNum = RandomWithExclusion(0, yourItem.Length, lastRandomNum);
}
//Do something with the lastRandomNum value below
newBall = theObjectballPools[lastRandomNum].GetPooledObject();
}
int RandomWithExclusion(int min, int max, int exclusion)
{
var result = UnityEngine.Random.Range(min, max - 1);
return (result < exclusion) ? result : result + 1;
}
Try linq :
public class Ball
{
public static List<Ball> balls = new List<Ball>();
public int value { get; set; }
public Boolean active { get; set; }
public Ball() {}
public Ball(int size)
{
// initial class
Random rand = new Random();
for (int i = 0; i < size; i++)
{
balls.Add(new Ball(){ value = rand.Next(), active = false});
}
}
public Ball GetRandom()
{
Random rand = new Random();
Ball randomBall = balls.Where(x => x.active == false).Select((x) => new { value = x, randX = rand.Next() }).OrderBy(x => x.randX).FirstOrDefault().value;
randomBall.active = true;
return randomBall;
}
}

not all code paths return a "value"

Hi I am trying to make a mastermind game where I having the user guess a number sequence between 4-10 instead of colours but for some reason my GetRandomNumberCount and my GenerateRandomNumber give me the error not all code paths return a value.
Any guidance would be appreciated
public static int GetRandomNumberCount()
{
//Create the secret code
Random RandomClass = new Random();
int first = RandomClass.Next(1, 5);
int second = RandomClass.Next(1,5);
int third = RandomClass.Next(1,5);
int forth = RandomClass.Next(1,5);
Console.WriteLine ("You are playing with M#sterB#t");
Console.WriteLine ("Bot Says : You Go First");
Console.WriteLine("Game Settings ");
Console.WriteLine("The Game Begins");
}
That's because they don't return a value. For example GetRandomNumberCount has Intset as its return type, but has no return statement. If you want to return nothing set the return type to void. Using that as an example
public static int[] GetRandomNumberCount()
{
//Create the secret code
Random RandomClass = new Random();
int first = RandomClass.Next(1, 5);
int second = RandomClass.Next(1,5);
int third = RandomClass.Next(1,5);
int forth = RandomClass.Next(1,5);
Console.WriteLine ("You are playing with M#sterB#t");
Console.WriteLine ("Bot Says : You Go First");
Console.WriteLine("Game Settings ");
Console.WriteLine("The Game Begins");
//This is where you would return a value, but in this case it seems you want to return an array of ints
//Notice how I changed the return type of the method to Int[]
int[] numbers = new int[4];
numbers.Add(first);
numbers.Add(second);
numbers.Add(third);
numbers.Add(fourth);
//This is the actual return statement that your methods are missing
return numbers;
}
regardless of whether you actually want to return an int array is moot though, Im only guessing. The real take away is that the int[] in
public static int[] GetRandomNumberCount()
declares a return type meaning you need a a return statement.
You are getting that error cause your method signature says it returns a int but your are not returning anything. What I see is, you meant to have a method with void return type like below since you are just printing the lines
public static void GetRandomNumberCount()
{
//Create the secret code
Random RandomClass = new Random();
int first = RandomClass.Next(1, 5);
int second = RandomClass.Next(1,5);
int third = RandomClass.Next(1,5);
int forth = RandomClass.Next(1,5);
Console.WriteLine ("You are playing with M#sterB#t");
Console.WriteLine ("Bot Says : You Go First");
Console.WriteLine("Game Settings ");
Console.WriteLine("The Game Begins");
}

Monty Hall Program Simulation (C#)

I am trying to simulate the Monty Hall Problem (because I read from the book Think Statistics that a guy in particular was convinced only after seeing a computer simulation) in C#, the programming language most familiar to me. My scenario is such that the position of the prize is random (in each run), my choice is random, and the game host's choice of opening the door is random (it can't be random if I picked the non-prize).
Surprisingly though, my program achieves the result of a 50:50 chance of winning no matter whether I switch or not. Here's the code to it (pardon me for the lengthiness):
class Program
{
static void Main(string[] args)
{
Random rand = new Random();
int noSwitchWins = RunGames(rand, false, 10000);
int switchWins = RunGames(rand, true, 10000);
Console.WriteLine(string.Format("If you don't switch, you will win {0} out of 1000 games.", noSwitchWins));
Console.WriteLine(string.Format("If you switch, you will win {0} out of 1000 games.", switchWins));
Console.ReadLine();
}
static int RunGames(Random rand, bool doSwitch, int numberOfRuns)
{
int counter = 0;
for (int i = 0; i < numberOfRuns; i++)
{
bool isWin = RunGame(rand, doSwitch);
if (isWin)
counter++;
}
return counter;
}
static bool RunGame(Random rand, bool doSwitch)
{
int prize = rand.Next(0, 2);
int selection = rand.Next(0, 2);
// available choices
List<Choice> choices = new List<Choice> { new Choice(), new Choice(), new Choice() };
choices[prize].IsPrize = true;
choices[selection].IsSelected = true;
Choice selectedChoice = choices[selection];
int randomlyDisplayedDoor = rand.Next(0, 1);
// one of the choices are displayed
var choicesToDisplay = choices.Where(x => !x.IsSelected && !x.IsPrize);
var displayedChoice = choicesToDisplay.ElementAt(choicesToDisplay.Count() == 1 ? 0 : randomlyDisplayedDoor);
choices.Remove(displayedChoice);
// would you like to switch?
if (doSwitch)
{
Choice initialChoice = choices.Where(x => x.IsSelected).FirstOrDefault();
selectedChoice = choices.Where(x => !x.IsSelected).FirstOrDefault();
selectedChoice.IsSelected = true;
}
return selectedChoice.IsPrize;
}
}
class Choice
{
public bool IsPrize = false;
public bool IsSelected = false;
}
This is entirely for my own interest's sake, and I wrote it in the way most familiar and comfortable to me. Do feel free to offer your own opinion and critique, thank you very much!
rand.Next(0,2)
only returns 0 or 1; the upper bound is exclusive. You are never picking the third door (unless you switch), and the third door never has the prize. You are modelling the wrong problem.
Try instead:
rand.Next(0,3)
Likewise:
int randomlyDisplayedDoor = rand.Next(0, 1);
only ever selects the first of the candidate doors; should be:
int randomlyDisplayedDoor = rand.Next(0, 2);
Now we get:
If you don't switch, you will win 3320 out of 1000 games.
If you switch, you will win 6639 out of 1000 games.
Note - the upper bound is inclusive when equals - i.e. rand.Next(1,1) always returns 1.
See Random.Next(minValue, maxValue)
Parameters
minValue
Type: System.Int32
The inclusive lower bound of the random number returned.
maxValue
Type: System.Int32
The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue.
To add to Marc's answer, you can also use Random.Next(Int32) since your lower bound is 0, so it would be simply:
rand.Next(3)

Categories

Resources