Factorio Style Power Line in Unity - c#

TLDR; How would you make power lines without infinite recursion?
I am currently working on a game where you place buildings and power lines between them to transfer power from coal factories to buildings that need power. Is there an algorithm or specific way of programming power lines so that they can transfer power between themselves and to and from other buildings. The main problem is doing this without having energy build up and exponentially increase. Considering there can be any number of power lines connected to any other amount, this is difficult.
Any tips or help?

private void GetTotalConnections(GameObject line, List<GameObject> checker) // HOLY SHIT IT WORKS HOLY SHIT IT WORKS HOLY SHIT IT WORKS
{
PowerLine temp = line.GetComponent<PowerLine>();
if (checker.Contains(line))
{
if (CheckIfConnectionsContains(checker, line.GetComponent<PowerLine>()) == true)
{
return;
}
}
for (int i = 0; i < temp.connectedLines.Count; i++)
{
if (checker.Contains(temp.connectedLines[i]) == false)
{
checker.Add(temp.connectedLines[i]);
GetTotalConnections(temp.connectedLines[i], checker);
}
else // if its already in the list, skip this entry since we already checked that it still had some new connections
{
continue;
}
}
}
private bool CheckIfConnectionsContains(List<GameObject> list, PowerLine line)
{
int count = 0;
for (int i = 0; i < line.connectedLines.Count; i++)
{
if (list.Contains(line.connectedLines[i]))
{
count++;
}
}
if (count == line.connectedLines.Count) // if every connection is already in the list
{
return true;
}
return false;
}

Related

Prevent System.StackOverflowException when recursively calling a method

I am new to c# (or coding in general) and I guess this question is really stupid and confusing (I know I'm doing it a hard way) but please help me.
I'm trying to make a minesweeper with form app. I made a 10 x 10 of buttons and if you click it, number of mines around it will be revealed. If a mine is there "F" (the first letter of "False") will appear.
There's a constructor that contains the button, x and y position, list of surrounding blocks, number of mines around it, and a boolean that indicates if there's a mine or not.
What I tried to do was to make the 8 surrounding blocks (from the list) cleared when the player clicked a block with no mine around it and if the block surrounding that block also doesn't have any mine around it, these blocks that surrounding that block will also be cleared. The method uses foreach to reveal and check the number of mines around that block. If there's no mines, same method will be applied to that block (calling the method recursively). The problem is that I keep getting System.StackOverflowException.
I somehow understand why it's happening but I just can't come up with the other way.
//scroll to the bottom for the method with the problem
private void Form1_Load(object sender, EventArgs e)
{
Random random = new Random();
Button[,] buttons = new Button[10, 10]
{
{ r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, r0c7, r0c8, r0c9 },
{ r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, r1c7, r1c8, r1c9 },
{ r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, r2c6, r2c7, r2c8, r2c9 },
{ r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, r3c7, r3c8, r3c9 },
{ r4c0, r4c1, r4c2, r4c3, r4c4, r4c5, r4c6, r4c7, r4c8, r4c9 },
{ r5c0, r5c1, r5c2, r5c3, r5c4, r5c5, r5c6, r5c7, r5c8, r5c9 },
{ r6c0, r6c1, r6c2, r6c3, r6c4, r6c5, r6c6, r6c7, r6c8, r6c9 },
{ r7c0, r7c1, r7c2, r7c3, r7c4, r7c5, r7c6, r7c7, r7c8, r7c9 },
{ r8c0, r8c1, r8c2, r8c3, r8c4, r8c5, r8c6, r8c7, r8c8, r8c9 },
{ r9c0, r9c1, r9c2, r9c3, r9c4, r9c5, r9c6, r9c7, r9c8, r9c9 }
};
Square[,] squares = new Square[10, 10];
for (int i = 0, ii = 0, iii = 0; i < 100; i++, ii++)
{
if (ii == 10)
{
ii = 0;
iii++;
}
squares[ii, iii] = new Square(i, buttons[ii, iii], ii, iii, 0, true);
}
List<int> randoms = new List<int>();
for (int i = 0; i < 10; i++)
{
int ii = random.Next(100);
if (!randoms.Contains(ii))
{
squares[ii % 10, ii / 10].setSafe(false);
}
else
{
i--;
}
randoms.Add(ii);
}
for (int i = 0; i < 10; i++)
{
for (int ii = 0; ii < 10; ii++)
{
for (int iii = -1; iii < 2; iii++)
{
for (int iiii = -1; iiii < 2; iiii++)
{
try
{
if (squares[i + iii, ii + iiii].getSafe() == false)
squares[i, ii].addNumber();
}
catch (System.IndexOutOfRangeException)
{
}
}
//if (squares[i, ii].getSafe() == false) squares[i, ii].getButton().Text = squares[i, ii].getSafe().ToString();
//else squares[i, ii].getButton().Text = squares[i, ii].getNumber().ToString();
}
}
}
for (int i = 0; i < 10; i++)
{
for (int ii = 0; ii < 10; ii++)
{
for (int iii = -1; iii < 2; iii++)
{
for (int iiii = -1; iiii < 2; iiii++)
{
try
{
squares[i, ii].addList(squares[i + iii, ii + iiii]);
}
catch (System.IndexOutOfRangeException)
{
}
}
}
}
}
}
Here's the Square class:
public class Square
{
int id;
Button button;
int x;
int y;
int number;
bool safe;
List<Square> list = new List<Square>();
public Square(int id, Button button, int x, int y, int number, bool safe)
{
this.id = id;
this.button = button;
this.x = x;
this.y = y;
this.number = number;
this.safe = safe;
button.Text = "";
button.Click += button_Click;
}
public int getId()
{
return id;
}
public void setId(int i)
{
id = i;
}
public Button getButton()
{
return button;
}
public void setButton(Button b)
{
button = b;
}
public int getX()
{
return x;
}
public void setX(int i)
{
x = i;
}
public int getY()
{
return y;
}
public void setY(int i)
{
y = i;
}
public int getNumber()
{
return number;
}
public void setNumber(int i)
{
number = i;
}
public void addNumber()
{
number++;
}
public bool getSafe()
{
return safe;
}
public void setSafe(bool b)
{
safe = b;
}
private void button_Click(object sender, EventArgs e)
{
if (getSafe() == false) button.Text = getSafe().ToString();
else button.Text = getNumber().ToString();
if (getNumber() == 0) zeroReveal();
}
//---------------------------------------------------
// this is the method that reveals surrounding blocks
//---------------------------------------------------
private void zeroReveal()
{
foreach (Square s in list)
{
//revealing the blocks
s.getButton().Text = s.getNumber().ToString();
//call the same method if there's no mine
//this is the line that keeps giving me exception
if (s.getNumber() == 0) s.zeroReveal();
}
}
//-----------------------------------------------------
public List<Square> getList()
{
return list;
}
public void setList(List<Square> sl)
{
list = sl;
}
public void addList(Square s)
{
list.Add(s);
}
}
I am new to c# (or coding in general) and I guess this question is really stupid and confusing (I know I'm doing it a hard way)
This topic confuses many a new developer; don't stress out about it!
If there's no mines, same method will be applied to that block (calling the method recursively).
Recursive methods can be confusing but if you design them using the standard pattern, you will avoid SO exceptions. You have not designed yours using the standard pattern.
The standard pattern for successful recursive methods is:
Am I in a case that requires no recursion?
If yes, do the necessary computations to produce the desired effect and return. The problem is now solved.
If no, then we're going to recurse.
Break the current problem down into some number of smaller problems.
Solve each smaller problem by recursing.
Combine the solutions of the smaller problem to solve the current problem.
The problem is now solved, so return.
The most important thing about designing a recursive method is that each recursion must be solving a smaller problem, and the sequence of smaller problems must bottom out at a case that does not require recursion. If those two conditions are not met, then you will get a stack overflow.
Internalize that pattern, and every time you write a recursive method, actually write it out:
int Frob(int blah)
{
if (I am in the base case)
{
solve the base case
return the result
}
else
{
find smaller problems
solve them
combine their solutions
return the result
}
}
Fill in that template with your real code, and you will be much more likely to avoid stack overflows. I've been writing recursive methods for decades, and I still follow this pattern.
Now, in your example, what is the case that does not require recursion? There must be one, so write down what it is. Next, how will you guarantee that the recursion solves a smaller problem? That is often the hard step! Give it some thought.
The stack overflow is occurring because zeroReveal is recursively calling itself forever. To fix this we need to find ways where we do not need it to make further calls to itself.
The name of the method gives us a clue. If the square has already been revealed, then surely the method does not need to do anything, since it has already been revealed.
It looks like the button's Text property is an empty string if it has not yet been revealed. So change the foreach so that it doesn't process squares that have already been revealed:
foreach (Square s in list)
{
if (s.getButton().Text == ""))
{
// existing code in the foreach loop goes here
}
}

I need to reset y inside the loop before the if statements, then check the count

Hello im trying to make this loop count when it finds a certain object then once it finds all 3 it executes the final line. However when i run the code as is the game does not start. Im assuming this is because once it cant find one object it counts that 3 times. So i believe the best way to fix this would be to reset y inside the loop before the if statements, then check the count. Im not exactly sure how to go about this. Any ideas of how i can make this happen. any help would be greatly appreciate it.
void Start() {
y = 0;
StartCoroutine(checkTurrets());
}
IEnumerator checkTurrets() {
while (true) {
y = 0;
if (GameObject.Find("right") != null) {} else {
y++;
}
y = 0;
if (GameObject.Find("left") != null) {} else {
y++;
}
y = 0;
if (GameObject.Find("bottom") != null) {} else {
y++;
}
if (y == 3) {
y++;
SphereCollider sc = gameObject.AddComponent(typeof (SphereCollider)) as SphereCollider;
}
}
}
Try This
IEnumerator checkTurrets() {
while (true) {
if (GameObject.Find("right") != null && GameObject.Find("left") != null && GameObject.Find("bottom") != null)
{
SphereCollider sc = gameObject.AddComponent(typeof (SphereCollider)) as SphereCollider;
}
}
}
I am assuming you want to execute the statement when you find all three objects.

Manually terminate recursion

I have written a recursion (actually I found the recursion online) to get all possible permutations of a set of numbers but in some occasions, due to the large number of possible permutations, I would like to add an If statement to terminate the recursion before it goes through all permutations. I have tried entering a return statement but it doesn't seem to work.
I am a bit of a newbie in coding so apologies if the answer obvious to everyone, I just cannot get it.
class EntryPoint
{
static void Main()
{
//input an initial sequence of the trains to schedule
Console.Write("Input train permutation");
string inputLine = Console.ReadLine();
GenerateTrainOrder GTO = new GenerateTrainOrder();
GTO.InputSet = GTO.MakeCharArray(inputLine);
GTO.CalcPermutation(0);
}
}
class GenerateTrainOrder
{
private int elementLevel = -1; // elements examined iterates immediately after the CalcPermutation initiates so we must set it equal -1 to start from 0
private int[] permutationValue = new int[0];
public int[,] Paths = new int[ParametersClass.timetableNumber, ParametersClass.trainsToSchedule];
private char[] inputSet;
public char[] InputSet
{
get { return inputSet; }
set { inputSet = value; }
}
private int permutationCount = 0;
public int PermutationCount
{
get { return permutationCount; }
set { permutationCount = value; }
}
//transform the input from the console to an array for later use
public char[] MakeCharArray(string InputString)
{
char[] charString = InputString.ToCharArray();
Array.Resize(ref permutationValue, charString.Length);
return charString;
}
public void CalcPermutation(int k)
{
elementLevel++;
permutationValue.SetValue(elementLevel, k);
//if we have gone through all the elements which exist in the set, output the results
if (elementLevel == ParametersClass.trainsToSchedule)
{
OutputPermutation(permutationValue); // output TrainOrder by passing the array with the permutation
}
//if there are elements which have not been allocated a place yet
else
{
for (int i = 0; i < ParametersClass.trainsToSchedule; i++)
{
//iterate until we come upon a slot in the array which has not been allocated an elements yet
if (permutationValue[i] == 0)
{
CalcPermutation(i); //rerun the code to allocate an element to the empty slot. the location of the empty slot is given as a parameter (this is how k increments)
}
}
}
elementLevel--;
permutationValue.SetValue(0, k);
}
private void OutputPermutation(int[] value)
{
int slot = 0;
foreach (int i in value)
{
Paths[permutationCount, slot] = Convert.ToInt16(Convert.ToString(inputSet.GetValue(i-1)));
slot++;
}
PermutationCount++;
}
}
It is a bit tricky to stop a recursive calculation. The best method involves a global variable (or a private class variable if you have objects).
This variable says if the recursion should stop or not:
bool stopRecursion = false;
Now inside your calculation you update this variable after each step:
if(_my_condition_is_met) stopRecursion = true;
At the start of your recursively called method you check the state of this variable:
public void CalcPermutation(int k)
{
if(stopRecursion) return;
...
And you check this variable after each call to CalcPermutation:
for (int i = 0; i < ParametersClass.trainsToSchedule; i++)
{
//iterate until we come upon a slot in the array which has not been allocated an elements yet
if (permutationValue[i] == 0)
{
CalcPermutation(i);
if(stopRecursion) return;
....
With this method you unwind even deep call levels as soon as your stop condition is met.
Thanks for the replies everyone. I managed to make it work by doing the following:
Under the CalcPermutation(i) line, I wrote
if (permutationCount == ParametersClass.timetableNumber)
{
return;
}
I tried running it a few times with different inputs and it seems to work just fine.
for (int i = 0; i < ParametersClass.trainsToSchedule; i++)
{
//iterate until we come upon a slot in the array which has not been allocated an elements yet
if({your condition}){
break;
}
if (permutationValue[i] == 0)
{
CalcPermutation(i); //rerun the code to allocate an element to the empty slot. the location of the empty slot is given as a parameter (this is how k increments)
}
}

Implementing an intersection merge of 2 array lists

For homework assignment we have to program a Intersection merge of 2 ArrayLists. I have done it using the following code
public void Intersection()
{
foreach (object obj1 in Developed)
{
Apps xApp = (Apps)obj1;
foreach (object obj2 in DPloyed)
{
Apps yApp = (Apps)obj2;
if (xApp.CompareName(yApp) == 0)
{
Inter.Add(yApp);
}
}
}
}
I would like to implement it rather using the while loop but the following code seems to keep missing elements in the list. It puts the first elements in the new intersection list but once the length of developed is increased from 1 element to 5 elements or more it does not add the new elements.
public void Intersection()
{
int i = 0;
int j = 0;
while (i < Developed.Count && j < DPloyed.Count)
{
Apps curA = (Apps)Developed[i];
Apps curB = (Apps)DPloyed[j];
if (curA.CompareName(curB) == 0)
{
Inter.Add(curA);
i++;
j++;
}
else if (curA.CompareName(curB) < 0)
{
i++;
}
else
j++;
}
}
Any help as to why the while loop does not work would be appreciated.
Thanks
Do this
while (i < Developed.Count || j < DPloyed.Count)
because may be both list may be having different Count.
and you need to put extra checks inside loop for indexes so that you don't get Index out of Range Exception.
Problem was not in the actual code for the merges. Problem found in my compare methods.

Playing 30 sound effects at the same time repeatedly

I'm trying to play about 30 piano notes at the same time in my XNA application on Windows Phone 7.
I have imported and loaded the wave files like below
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
sound3 = Content.Load<SoundEffect>("1");
sound5 = Content.Load<SoundEffect>("3");
sound6 = Content.Load<SoundEffect>("4");
sound7 = Content.Load<SoundEffect>("5");
sound8 = Content.Load<SoundEffect>("6");
}
Every sound effect file is less than a second, so I'm trying to play all of them at the same time. I play them using a for loop which runs every second.(So at every loop 30 sounds will be played then it goes on and plays the same 30 sounds each second)
It works fine for a few seconds but suddenly it stops playing any sound (The loop is still working) then again starts working for once or twice and again stops working . It also sometimes makes some bad noises as if the audio system cant support too many sounds to be play at a time.
I'm not sure how I can solve the problem , if its a buffer problem or threads.
Nico Schertler is right! I've faced the same problem and fixed it by managing instances. Normally when you play soundeffect you always get the instance! By default you have to hope XNA/Monogame will care about it, but it doesn't. When you have to many live instances (even stopped, you get breaks, noise, even silence.
I hold rectangular array for all my sound effects with up to 4 instance for each, and if I have instances of a sound I want to play instead of creating I stop the oldest (by timestamp), play it and remember the current timestamp.
See the following code:
private const int MAX_INST_OF_ONE_SOUND = 4;
private SoundEffectInstance[][] sndInstArray = null;
private float[][] sndInstTimes = null;
public init()
{
sndArray = new SoundEffect[SoundsSchema.sounds.Length];
sndInstArray = new SoundEffectInstance[SoundsSchema.sounds.Length][];
sndInstTimes = new float[SoundsSchema.sounds.Length][];
for (int i = 0; i < SoundsSchema.sounds.Length; i++)
{
try
{
sndArray[i] = content.Load<SoundEffect>(SoundsSchema.sounds[i]);//SoundsSchema is string list holder class
}
catch (System.Exception)
{
}
sndInstArray[i] = new SoundEffectInstance[MAX_INST_OF_ONE_SOUND];
sndInstTimes[i] = new float[MAX_INST_OF_ONE_SOUND];
}
}
private SoundEffectInstance getValidInstance(int sound)
{
if (sound < 0 || sound > sndInstArray.Length)
return null;
SoundEffectInstance inst = null;
for (int i = 0; i < MAX_INST_OF_ONE_SOUND; i++)
{
if (sndInstArray[sound][i] == null || (sndInstArray[sound][i] != null && sndInstArray[sound][i].IsDisposed))
{
sndInstArray[sound][i] = sndArray[sound].CreateInstance();
sndInstTimes[sound][i] = MyEngine.CurTime;
inst = sndInstArray[sound][i];
break;
}
}
if (inst == null)
{
float min_time = float.MaxValue;
int ind = -1;
for (int i = 0; i < MAX_INST_OF_ONE_SOUND; i++)
{
if (sndInstArray[sound][i] != null && sndInstTimes[sound][i] < min_time)
{
min_time = sndInstTimes[sound][i];
ind = i;
}
}
if (ind == -1)
ind = 0;
if (sndInstArray[sound][ind].IsDisposed)
sndInstArray[sound][ind] = sndArray[sound].CreateInstance();
else
{
try
{
sndInstArray[sound][ind].Stop();
}
catch
{}
}
sndInstTimes[sound][ind] = MyEngine.CurTime;
inst = sndInstArray[sound][ind];
}
return inst;
}
public virtual void playSound(int sound, float volume, float panoram, bool loop)
{
if (sound < 0 || sound > sndArray.Length)
return null;
if (!mMuted && mVolume > 0)
{
SoundEffectInstance sndinst = getValidInstance(sound);
if (sndinst == null)
return null;
try
{
sndinst.IsLooped = loop;
sndinst.Pan = panoram;
sndinst.Volume = mVolume * vol;
sndinst.Play();
}
catch
{
}
}
}

Categories