Multithreading and passing data to new threads - c#

I have some issues with an application I'm coding. It's job is to solve a maze using threads. One thread begins, and for each bifurcation it calls a static method in another class, passing parameters that the other thread needs, and then starts threads for each path. My outputs are all messed up, and I'm not sure if it's a Multithreading problem or maybe a problem with the references. Here's some of the code (Each thread is given a new instance of an Explorer class):
This is the Run() method for each thread:
public void Explore()
{
while (ImDone == false)
{
Move();
if (ActualPosition[0] != Labyrinth.ExitPoint[0] ||
ActualPosition[1] != Labyrinth.ExitPoint[1]) //I'm not at the end..
continue;
PrintMyStatus(); //Print in the console my parents and my complete route..
break;
}
This is the Move() method:
List<int[]> validPaths = CheckSurroundings(); //returns a list of paths
switch (validPaths.Count)
{
case 0:
ImDone = true; //No more routes available
break;
case 1:
MarkMyPosition(validPaths); //Change internal variables to the only new path
break;
default:
lock(this) //As this involves thread creating I locked it..
{
//Creating deep copies of my "History" so my childs can have them..
List<int[]> DCValidPaths = DeepCopy(validPaths);
List<string> DCMyParents = DeepCopy(MyParents);
string[,] DCMyExplorationMap = DeepCopy(MyExplorationMap);
List<int[]> DCMyPositions = DeepCopy(MyPositions);
foreach (var path in validPaths)
{
DCMyParents.Add(Thread.CurrentThread.Name); //Adding myself as a parent
ExplorationManager.ExplorerMaker(
DCValidPaths,
DCMyParents,
DCMyExplorationMap,
DCMyPositions,
ID); //Threads are created in the static class
}
}
break;
}
My DeepCopy() method is using is the one in the accepted answer of this question. Any other info needed I'll be more than happy to provide in order to solve the problem. Thanks in advance for your help.
EDITS: My problem consists basically in: I'm having an OutOfMemoryException in the Thread.Start() clause and the Path displayed by the threads contain corrupted data (incorrect positions). I tested the CheckSurroundings() method and by far only returns correct positions (The labyrinth is contained in a two-dimensional string array).
This is the constructor of the Explorer class:
public Explorer(List<string> myParents, int[] actualPosition, string[,] myExplorationMap,
List<int[]> myPositions, int ID)
{
//Here I pass the data specified in Move();
MyParents = myParents;
ActualPosition = actualPosition;
MyExplorationMap = myExplorationMap;
MyPositions = myPositions;
this.ID = ID + 1; //An ID for reference
//Marking position in my map with "1" so I know I've been here already,
//and adding the position given to my list of positions
MyExplorationMap[ActualPosition[0], ActualPosition[1]] = "1";
MyPositions.Add(DeepCopy(ActualPosition));
}
And this is the class creating the threads:
public static class ExplorationManager
{
public static void ExplorerMaker(List<int[]> validPaths, List<string> myParents, string[,] myExplorationMap, List<int[]> myPositions, int ID)
{
foreach (var thread in validPaths.Select
(path => new Explorer(myParents, path, myExplorationMap, myPositions,ID)).
Select(explorer => new Thread(explorer.Explore)))
{
thread.Name = "Thread of " + ID + " generation";
thread.Start(); //For each Path in Valid paths, create a new instance of Explorer and assign a thread to it.
}
}
}
And the method returning the ValidPaths
private List<int[]> CheckSurroundings()
{
var validPaths = new List<int[]>();
var posX = ActualPosition[0];
var posY = ActualPosition[1];
for (var dx = -1; dx <= 1; dx++)
{
if (dx == 0 || (posX + dx) < 0 || (posX + dx) >= Labyrinth.Size ||
MyExplorationMap[posX + dx, posY] == "1") continue;
var tempPos = new int[2];
tempPos[0] = posX + dx;
tempPos[1] = posY;
validPaths.Add(tempPos);
}
for (var dy = -1; dy <= 1; dy++)
{
if (dy == 0 || (posY + dy) < 0 || (posY + dy) >= Labyrinth.Size ||
MyExplorationMap[posX, posY + dy] == "1") continue;
var tempPos = new int[2];
tempPos[0] = posX;
tempPos[1] = posY + dy;
validPaths.Add(tempPos);
}
//This method checks up, down, left, right and returns the posible routes as `int[]` for each one
return validPaths;
}
CheckSurroundings uses the deep copy passed to the child (via the constructor) to validate the routes he can take. It is NOT intended to alterate the parent's copy, because he is now in ANOTHER path of the labyrinth. The child only needs the information updated (passed via the constructor) UNTIL the point they "separate". And also EACH child must be independent from the other childs. That's what I'm trying to do. But I'm not sure what's wrong, maybe concurrency problem? Please help. If you need anything else let me know. –

EDIT for your updates:
myExplorationMap is a deep copy of the original exploration map. You set the position to 1 in the Explorer constructor, which updates the copy that is shared by all of the child threads, but it does not update the original MyExplorationMap property in the parent thread. Only the child threads will know this position was visited. I assume this is used somewhere in the CheckSurroundings method?

Related

A* Path-finding is just barely not working

I have my NPC and I have a target location for him. I have placed my A* path-finding algorithm into the code. This created a path, but it is in no way an ideal path, nor is it cohesive. I have written this piece a few times and have found myself facing the same problem.
This picture should show the result of my problem: the final path is all over the place.
To me, it appears that algorithm is finding a connection between every tile on my map instead of locating the best path. I really can't nail down where this is happening, though.
For some clarity, I am defining the tiles (squares) and their neighbors when I load my game scene. When the program reaches GetAdjacentSquares is is using those results to find valid tiles. You will notice that they NPC's path is not centering directly onto any of the green tiles.
List<Path> GetAdjacentSquares(Path p)
{
List<Path> ret = new List<Path>();
TileData tile = tManager.GetTileByCoords(new Vector2(p.x, p.y));
foreach (Vector2 t in tile.neighborLocs)
{
TileData n = tManager.GetTileByCoords(t);
if (n && n.tType == TileTypes.Houses || n.tType == TileTypes.Road)
{
ret.Add(new Path(p, n.tTileLoc.x, n.tTileLoc.y));
}
}
return ret;
}
int BlocksToTarget(Vector2 tileLoc, Vector2 targetLoc)
{
int final = (int)Mathf.Abs((tileLoc.x - targetLoc.x) * (tileLoc.x - targetLoc.x) + (tileLoc.y - targetLoc.y) * (tileLoc.y - targetLoc.y));
return final;`
}
bool DoesPathContain(List<Path> paths, Vector2 target)
{
foreach(Path p in paths)
{
if (p.x == target.x && p.y == target.y)
return true;
}
return false;
}
int LowestFScore(List<Path> path)
{
int lowest = int.MaxValue;
foreach(Path p in path)
{
if (p.f <= lowest)
lowest = p.f;
}
return lowest;
}
void GoHome()
{
Path current = null;
//target
Path destination = new Path(null, cData.houseLoc.x, cData.houseLoc.y);
//start
Path start = new Path(destination, transform.localPosition.x, transform.localPosition.y);
//start by adding the original position to the open list
List<Path> open = new List<Path>() { start };
List<Path> close = new List<Path>();
int g = 0;
while(open.Count > 0)
{
//get the square with the lowest F score
current = open.Last(p => p.f == LowestFScore(open));
//add the current square to the closed list
close.Add(current);
//remove it from the open list
open.Remove(current);
//if we added the destination to the closed list, we've found a path
if(DoesPathContain(close, cData.houseLoc))
break;
//The rest of the algorithm evaluates adjacent tiles
List<Path> adjacentTiles = GetAdjacentSquares(current);
g++;
foreach(Path tile in adjacentTiles)
{
Vector2 tileLoc = new Vector2(tile.x, tile.y);
//if this adjacent square is already in the closed list, ignore it
if (DoesPathContain(close, tileLoc))
continue;
if(!DoesPathContain(open, tileLoc))
{
//if this adjacent square is already in the closed list, ignore it
tile.g = g;
tile.h = BlocksToTarget(tileLoc, cData.houseLoc);
tile.parent = current;
//add it to the open list
open.Add(tile);
}
else
{
//test if using the current G score makes the adjacent square's F score
//lower, if yes update the parent because it means it's a better path
if (g+tile.h < tile.f)
{
tile.g = g;
tile.parent = current;
}
}
}
}
//foreach (Path p in close)
// Debug.Log(p.f + " ("+p.x+", "+p.y+")");
walkTo = close;
cData.isWalking = true;
}
`
I have learned that I was getting my final results from the closed list. Once I accessed the correct list, the path was drawn!

Using Audio Graph, learn environment noise and filter in a UWP App

This is an extended question from here Using UWP monitor live audio and detect gun-fire/clap sound
Thanks to Dernis I finally got the code working to monitor live audio and trigger events when decibel count is above a certain range.
This works perfectly when we run it in office/closed/silent area.
But when I take the app to open road, there will be traffic sound, wind sound, people talk sound and other noises and BLOW events are not identified correctly.
I would like to implement something like Lean Environment button. Before app starts monitoring, the user clicks on "Lean Environment" that recognize the sensitivity levels and set filtering to my live audio and then I start monitoring blows.
If it doesn't add too much load, I would like to record the audio to a file.
Any help on where to start would be appreciated.
OnNavigatedTo
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
//other logic
await CreateInputDeviceNodeAsync(_deviceId);
}
CreateInputDeviceNodeAsync
public async Task<bool> CreateInputDeviceNodeAsync(string deviceId)
{
Console.WriteLine("Creating AudioGraphs");
// Create an AudioGraph with default settings
AudioGraphSettings graphSettings = new AudioGraphSettings(AudioRenderCategory.Media)
{
EncodingProperties = new AudioEncodingProperties
{
Subtype = "Float",
SampleRate = 48000,
ChannelCount = 2,
BitsPerSample = 32,
Bitrate = 3072000
}
};
CreateAudioGraphResult audioGraphResult = await AudioGraph.CreateAsync(graphSettings);
if (audioGraphResult.Status != AudioGraphCreationStatus.Success)
{
_rootPage.NotifyUser("Cannot create graph", NotifyType.ErrorMessage);
return false;
}
_audioGraph = audioGraphResult.Graph;
AudioGraphSettings audioGraphSettings =
new AudioGraphSettings(AudioRenderCategory.GameChat)
{
EncodingProperties = AudioEncodingProperties.CreatePcm(48000, 2, 32),
DesiredSamplesPerQuantum = 990,
QuantumSizeSelectionMode = QuantumSizeSelectionMode.ClosestToDesired
};
_frameOutputNode = _audioGraph.CreateFrameOutputNode(_audioGraph.EncodingProperties);
_quantum = 0;
_audioGraph.QuantumStarted += Graph_QuantumStarted;
LoudNoise += BlowDetected;
DeviceInformation selectedDevice = null;
if (!string.IsNullOrWhiteSpace(_deviceId))
selectedDevice = await DeviceInformation.CreateFromIdAsync(_deviceId);
if (selectedDevice == null)
{
string device = Windows.Media.Devices.MediaDevice.GetDefaultAudioCaptureId(
Windows.Media.Devices.AudioDeviceRole.Default);
if (!string.IsNullOrWhiteSpace(device))
selectedDevice = await DeviceInformation.CreateFromIdAsync(device);
else
{
_rootPage.NotifyUser($"Could not select Audio Device {device}", NotifyType.ErrorMessage);
return false;
}
}
CreateAudioDeviceInputNodeResult result =
await _audioGraph.CreateDeviceInputNodeAsync(MediaCategory.Media, audioGraphSettings.EncodingProperties,
selectedDevice);
if (result.Status != AudioDeviceNodeCreationStatus.Success)
{
_rootPage.NotifyUser("Cannot create device output node", NotifyType.ErrorMessage);
return false;
}
_selectedMicrophone = selectedDevice.Name;
_deviceInputNode = result.DeviceInputNode;
_deviceInputNode.AddOutgoingConnection(_frameOutputNode);
_frameOutputNode.Start();
_audioGraph.Start();
return true;
}
Graph_QuantumStarted
private void Graph_QuantumStarted(AudioGraph sender, object args)
{
if (++_quantum % 2 != 0) return;
AudioFrame frame = _frameOutputNode.GetFrame();
float[] dataInFloats;
using (AudioBuffer buffer = frame.LockBuffer(AudioBufferAccessMode.Write))
using (IMemoryBufferReference reference = buffer.CreateReference())
unsafe
{
// Get the buffer from the AudioFrame
// ReSharper disable once SuspiciousTypeConversion.Global
((IMemoryBufferByteAccess) reference).GetBuffer(out byte* dataInBytes,
out var capacityInBytes);
var dataInFloat = (float*) dataInBytes;
dataInFloats = new float[capacityInBytes / sizeof(float)];
for (var i = 0; i < capacityInBytes / sizeof(float); i++)
{
dataInFloats[i] = dataInFloat[i];
}
}
double decibels = dataInFloats.Aggregate<float, double>(0f, (current, sample) => current + Math.Abs(sample));
decibels = 20 * Math.Log10(decibels / dataInFloats.Length);
_decibelList.Add(decibels);
if (double.IsInfinity(decibels) || decibels < _threshold) return;//-45
if (_watch != null && _watch.Elapsed <= TimeSpan.FromSeconds(1)) return;
LoudNoise?.Invoke(this, decibels);
_watch = Stopwatch.StartNew();
}
This is just statistics. You'll want to collect probably at least 50 frames (1 second) of data before actually having it function (maybe let the user decide by holding and releasing a button). Then you'll probably want to determine where the decibel level is usually around. I can think of 3 ways to do that.
private void Graph_QuantumStarted(AudioGraph sender, object args)
{
...
double decibels = dataInFloats.Aggregate<float, double>(0f, (current, sample) => current + Math.Abs(sample)); // I dislike the fact that the decibels variable is initially inaccurate, but it's your codebase.
decibels = 20 * Math.Log10(decibels / dataInFloats.Length);
if (scanning) // class variable (bool), you can set it from the UI thread like this
{
_decibelList.Add(decibels); // I assume you made this a class variable
}
else if (decibels == Double.NaN)
{
// Code by case below
}
else if (decibels > _sensitivity) //_sensitivity is a class variable(double), initialized to Double.NaN
{
LoudNoise?.Invoke(this, true); // Calling events is a wee bit expensive, you probably want to handle the sensitivity before Invoking it, I'm also going to do it like that to make this demo simpler
}
}
If you can control make sure there's no spike loud enough you want it to go off you can just take the max value of all those frames and say if it's over the sensitivity is maxDecibels + Math.Abs(maxDecibels* 0.2) (the decibels could be negative, hence Abs).
double maxDecibels = _decibelList.OrderByDescending(x => x)[0];
_sensitivity = maxDecibels + Math.Abs(maxDecibels* 0.2);
If you can't control when there's a spike, then you could collect those frames, sort, and have it take item [24] (of your 100 item list) and say that's the sensitivity.
sensitivity = _decibelList.OrderByDescending(x => x)[24]; // If you do a variable time you can just take Count/4 - 1 as the index
(I think it's the best but I really don't know statistics) Walk the list of frame's decibels and track the average difference in value and the what index changed it most. Afterwards, find the max value from after that index and say 75% of the change to there is the sensitivty. (Don't use a LinkedList on this)
int greatestChange, changeIndex = 0;
double p = Double.NaN; // Previous
for (int i = 0; i < _decibelList.Count(); i++)
{
if (p != Double.Nan)
{
change = Math.Abs(_decibelList[i] - p);
if (Math.Abs(change > greatestChange)
{
greatestChange = change;
changeIndex = i;
}
}
p = _decibelList[i];
}
int i = changeIndex;
p = Double.NaN; // reused
double c= Double.NaN; // Current
do
{
p = c != Double.NaN ? c : _decibelList[i];
c = _decibelList[++i];
} while (c < p);
_sensitivity = ((3 * c) + _decibelList[changeIndex]) / 4;
Note: You can (kind of) remove the need to sort by having a LinkedList and inserting in the appropiate place

How to copy one array items to another array in unity Using C#? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have two GameObject arrays with same length. I am trying to copy same values of array1 to array2.
I tried using system.Array.copy(Array1,Array2,4) and also tried Array1= Array2
but not working.
These two arrays are holding 4 buttons each with child text. I want show 4 answers that are assigned to these buttons and copy same answers to another 4 buttons at the same time. Something like dual player.
Can anyone please help?
public class DuelMode : MonoBehaviour{
public static DuelMode instance;
// these are the question values a and b
private int a, b, a1, b1;
//the variable for answer value
[HideInInspector] public int answer;
//varible whihc will assign ans to any one of the 4 answer button
private int locationOfAnswer;
//ref to the button
public GameObject[] ansButtons;
private GameObject[] ansButtonsDuel;
//ref to image symbol so player can know which operation is to be done
public Image mathSymbolObject;
//ref to all the symbol sprites whihc will be used in above image
public Sprite[] mathSymbols;
//get the tag of button
public string tagOfButton;
//ref to text in scene where we will assign a and b values of question
public Text valueA, valueB, valueA1, valueB1;
void Awake()
{
MakeInstance();
}
//method whihc make this object instance
void MakeInstance()
{
if (instance == null)
{
instance = this;
}
}
//at start we need to do few basic setups
void Start()
{
//we put the location value in tag of button variable
tagOfButton = locationOfAnswer.ToString();
MathsProblem();
}
//this method keeps the track of mode
// Update is called once per frame
void Update()
{
tagOfButton = locationOfAnswer.ToString();
}
//Below code is for maths calculation
public void MathsProblem()
{
bool roundActive = true;
}
//this methode perform Multiplication process
void MultiplicationMethod()
{
bool reloop;
bool[] numbers = new bool[301];
b = b1 = Random.Range(1, 10);
locationOfAnswer = Random.Range(0, ansButtons.Length);
answer = a * b;
numbers[answer] = true;
if (valueA != null && valueB != null && valueA1 != null && valueB1 != null)
{
valueA.text = a.ToString();
valueB.text = b.ToString();
valueA1.text = a.ToString();
valueB1.text = b.ToString();
}
mathSymbolObject.sprite = mathSymbols[0];
for (int i = 0; i < ansButtons.Length; i++)
{
if (i == locationOfAnswer)
{
ansButtons[i].GetComponentInChildren<Text>().text = "" + answer;
}
else
{
// the below code make sure that all the values assigned to the ans button are within the range
if (a * b <= 100)
{
ansButtons[i].GetComponentInChildren<Text>().text = "" + Random.Range(1, 101);
}
else if (a * b <= 200 & a * b > 100)
{
ansButtons[i].GetComponentInChildren<Text>().text = "" + Random.Range(101, 201);
}
else if (a * b <= 300 & a * b > 200)
{
ansButtons[i].GetComponentInChildren<Text>().text = "" + Random.Range(201, 301);
}
else if (a * b <= 400 & a * b > 300)
{
ansButtons[i].GetComponentInChildren<Text>().text = "" + Random.Range(301, 401);
}
while (ansButtons[i].GetComponentInChildren<Text>().text == "" + answer)
{
ansButtons[i].GetComponentInChildren<Text>().text = "" + Random.Range(1, 401);
}
}
}
}
}
I assume when you are finished setting texts for ansButtons now you simply want to display the same texts on both sides, right?
for(int i = 0; i < ansButtons; i++)
{
// get the GameObjects
var ansButton = ansButtons[i];
var ansDuelButton = ansButtonsDuel[i];
// get the Text components
var ansText = ansButton.GetComponentInChildren<Text>(true);
var duelText = ansDuelButton .GetComponentInChildren<Text>(true);
// copy the text over
duelText.text = ansText.text;
}
I asume ofcourse you have all the references in ansButtons and ansButtonsDuel setup e.g. via the inspector or while Instantiate the objects.
Please can you post example of your code and the object array. This will help us understand the problem so we can then resolve the issue.
You can also have a look at the post below. It may help your cause.
Difference Between Array.CopyTo() and Array.CloneTo()
EDIT:
var foo = new String[] {"ab" , "cd", "ef"} ;
var bar = new List<string>();
foreach(var item in foo)
{
bar.Add(item);
}
var barArray = bar.ToArray();
for(int i = 0; i < foo.Length; i++)
{
Console.WriteLine(foo[i]);
Console.WriteLine(barArray[i]);
}
The code above makes use of list and arrays since that way you will not have to index your duplicate array. You can run the code in dot net fiddle to see the output. I have used string in this example, please replace with your object.

System.StackOverflowException error

I am trying to create a 2D cave generation system. When I run the program I get "System.StackOverflowException" Exception, after I try to create new object from its own class.
My cave generator works like this:
I create a map that contains ID’s (integers) of the different types of cells(like wall, water or empty Space).
First off all my "Map" class creates a map filled with walls and after that in the centre of the map, it creates a "Miner" object. The Miner digs the map and makes caves. The problem is I want to create more miners. So, my Miner that is digging the map creates another Miner. However, when I do this, I get a "System.StackOverflowException" Exception.
How do I go about tracking down the cause of the StackOverflow in my program.
Here is my miner code:
Miner.cs
public class Miner
{
Random rand = new Random();
public string state { get; set; }
public int x { get; set; }
public int y { get; set; }
public Map map { get; set; }
public int minersCount;
public Miner(Map map, string state, int x, int y)
{
this.map = map;
this.state = state;
this.x = x;
this.y = y;
minersCount++;
if (state == "Active")
{
StartDigging();
}
}
bool IsOutOfBounds(int x, int y)
{
if (x == 0 || y == 0)
{
return true;
}
else if (x > map.mapWidth - 2 || y > map.mapHeight - 2)
{
return true;
}
return false;
}
bool IsLastMiner()
{
if (minersCount == 1)
{
return true;
}
else
{
return false;
}
}
public void StartDigging()
{
if (state == "Active")
{
int dir = 0;
bool needStop = false;
int ID = -1;
while (!needStop && !IsOutOfBounds(x, y))
{
while (dir == 0)
{
dir = ChooseDirection();
}
if (!AroundIsNothing())
{
while (ID == -1)
{
ID = GetIDFromDirection(dir);
}
}
else
{
if (!IsLastMiner())
{
needStop = true;
}
}
if (ID == 1)
{
DigToDirection(dir);
dir = 0;
}
if (ID == 0 && IsLastMiner())
{
MoveToDirection(dir);
dir = 0;
}
TryToCreateNewMiner();
}
if (needStop)
{
state = "Deactive";
}
}
}
public void TryToCreateNewMiner()
{
if (RandomPercent(8))
{
Miner newMiner = new Miner(map, "Active", x, y);
}
else
{
return;
}
}
bool AroundIsNothing()
{
if (map.map[x + 1, y] == 0 && map.map[x, y + 1] == 0 &&
map.map[x - 1, y] == 0 && map.map[x, y - 1] == 0)
{
return true;
}
else
{
return false;
}
}
void MoveToDirection(int dir)
{
if (dir == 1)
{
x = x + 1;
}
else if (dir == 2)
{
y = y + 1;
}
else if (dir == 3)
{
x = x - 1;
}
else if (dir == 4)
{
y = y - 1;
}
}
void DigToDirection(int dir)
{
if (dir == 1)
{
map.map[x + 1, y] = 0;
x = x + 1;
}
else if (dir == 2)
{
map.map[x, y + 1] = 0;
y = y + 1;
}
else if (dir == 3)
{
map.map[x - 1, y] = 0;
x = x - 1;
}
else if (dir == 4)
{
map.map[x, y - 1] = 0;
y = y - 1;
}
}
int GetIDFromDirection(int dir)
{
if (dir == 1)
{
return map.map[x + 1, y];
}
else if (dir == 2)
{
return map.map[x, y + 1];
}
else if (dir == 3)
{
return map.map[x - 1, y];
}
else if (dir == 4)
{
return map.map[x, y - 1];
}
else
{
return -1;
}
}
int ChooseDirection()
{
return rand.Next(1, 5);
}
bool RandomPercent(int percent)
{
if (percent >= rand.Next(1, 101))
{
return true;
}
return false;
}
}
Whilst you can get StackOverflowExceptions by creating too many really large objects on the stack, it usually happens because your code has got into a state where it is calling the same chain of functions over and over again. So, to track down the cause in your code, the best starting point is to determine where your code calls itself.
Your code consists of several functions that are called by the Miner class itself, most of which are trivial
Trivial functions that don't call anything else in the class. Whilst these functions may contribute to the state that triggers the problem, they aren't part of the terminal function loop:
IsOutOfBounds(int x, int y)
bool IsLastMiner()
bool AroundIsNothing()
void MoveToDirection(int dir)
void DigToDirection(int dir)
int GetIDFromDirection(int dir)
int ChooseDirection()
bool RandomPercent(int percent)
This leaves your remaining three functions
public Miner(Map map, string state, int x, int y) // Called by TryToCreateNewMiner
public void StartDigging() // Called by constructor
// Contains main digging loop
public void TryToCreateNewMiner() // Called by StartDigging
These three functions form a calling loop, so if the branching logic in the functions is incorrect it could cause a non-terminating loop and hence a stack overflow.
So, looking at the branching logic in the functions
Miner
The constructor only has one branch, based on if the state is "Active". It is always active, since that's the way the object is always being created, so the constructor will always call StartDigging. This feels like the state isn't being handled correctly, although it's possible that you're going to use it for something else in the future...
As an aside, it's generally considered to be bad practice to do a lot of processing, not required to create the object in an objects constructor. All of your processing happens in the constructor which feels wrong.
TryToCreateNewMiner
This has one branch, 8% of the time, it will create a new miner and call the constructor. So for every 10 times TryToCreateNewMiner is called, we stand a good chance that it will have succeeded at least once. The new miner is initially started in the same position as the parent object (x and y aren't changed).
StartDigging
There's a fair bit of branching in this method. The main bit we are interested in are the conditions around calls to TryToCreateNewMiner. Lets look at the branches:
if(state=="Active")
This is currently a redundant check (it's always active).
while (!needStop && !IsOutOfBounds(x, y)) {
The first part of this termination clause is never triggered. needStop is only ever set to true if(!IsLastMiner). Since minersCount is always 1, it's always the last miner, so needStop is never triggered. The way you are using minersCount suggests that you think it is shared between instances of Miner, which it isn't. If that is your intention you may want to read up on static variables.
The second part of the termination clause is the only way out of the loop and that is triggered if either x or y reaches the edge of the map.
while(dir==0)
This is a pointless check, dir can only be a number between 1 and 5, since that's what is returned by ChooseDirection.
if(!AroundIsNothing())
This is checking if the positions that the Miner can move into are all set to 0. If they are not, then GetIDFromDirection is called. This is key. If the Miner is currently surrounded by 0, ID will not be set, it will remain at it's previous value. In a situation where a Miner has just been created, this will be -1 (we know this could happen because all Miners are created at the location of the Miner creating it).
The last two checksif(ID==1) and if(ID==0 && IsLastMiner()) guard the code that moves the Miner (either by calling dig, or move). So, if ID is not 0, or 1 at this point the Miner will not move. This could cause a problem because it is immediately before the call to TryToCreateNewMiner, so if the program ever gets into this situation it will be stuck in a loop where the Miner isn't moving and it's constantly trying to create new Miners in the same position. 8% of the time this will work, creating a new miner in the same position, which will perform the same checks and get into the same loop, again not moving and trying to create a new miner and so it goes until the stack runs out of space and the program crashes.
You need to take a look at your termination clauses and the way you're handling ID, you probably don't want the Miner to just stop doing anything if it gets completely surround by 0.

Space Invaders health/array issues

I've been doing some exercises to learn C#. I've been doing XNA and making a space invaders clone.
So far, everything is dandy, but I have come across some walls when coding.
The issues and the supporting code are as follows:
My top of row of invaders have 2 health points, take 2 hits to destroy and yield more points. However, when one is hit, and destroyed, the rest of the top of row are reduced to 1 HP, and take 1 hit to destroy - which is not my desired result.
The offending code I suspect is:
if (playerBullet != null && Type1Invaders != null)
{
Rectangle rectMissile = new Rectangle((int)playerBullet.getX(), playerBullet.getY(), playerBulletIMG.Width, playerBulletIMG.Height);
for (int count = 0; count < 11; count++)
{
Rectangle rectInvader = new Rectangle(Type1Invaders[count].getX(), Type1Invaders[count].getY(), invader1.Width, invader1.Height);
if (Type1Invaders[count].getVis() && rectMissile.Intersects(rectInvader))
{
Type1Invaders[count].setHP(Type1Invaders[count].getHP() - 1);
shootTimer = 0;
if (Type1Invaders[count].getHP() == 0)
{
explosionInstance.Play();
playerBullet = null;
Type1Invaders[count].setVis(false);
score = score + Type1Invaders[count].getPointValue();
}
}
}
}
My second error resides in how I'm detecting the leftmost and rightmost invaders in a row. When an entire row has been destroyed, I get a nullreferenceerror. (Those are a nightmare..)
Anyway, this is the offending code
The method of finding the left and right most invaders
var LeftMost5 = Type5Invaders.Where(i => i.getVis()).FirstOrDefault();
var RightMost5 = Type5Invaders.Where(i => i.getVis()).LastOrDefault();
And the if statement is throwing the null error
if (RightMost5.getX() >= RightGameEdge)
{
Type5.setDir(-1);
for (int count = 0; count < 11; count++)
{
invaderMoveInstance5.Play();
Type5Invaders[count].MoveY(8);
}
}
It only happens with the rightmost, but I can assume it will happen to the left too - I'm assuming I can apply the same logic to fix this error to the left side too.
I can supply more information and snippets if this is not sufficient.
Thanks in advance for the assistance!
For the first issue. I suppose that when a bullet kills an invader, you can say that that bullet won't kill another invader. Therefore, you can add a break; to stop looping. Like this:
if (Type1Invaders[count].getVis() && rectMissile.Intersects(rectInvader))
{
Type1Invaders[count].setHP(Type1Invaders[count].getHP() - 1);
shootTimer = 0;
if (Type1Invaders[count].getHP() == 0)
{
explosionInstance.Play();
playerBullet = null;
Type1Invaders[count].setVis(false);
score = score + Type1Invaders[count].getPointValue();
}
break;
}
For the second error, the FirstOrDefault method returns null in case your collection is empty (after you have killed all type 5 invaders). You simply need to check if it is null or not, like this:
var LeftMost5 = Type5Invaders.Where(i => i.getVis()).FirstOrDefault();
var RightMost5 = Type5Invaders.Where(i => i.getVis()).LastOrDefault();
if(RightMost5 != null)
{
// this means we have a non-null invader
if (RightMost5.getX() >= RightGameEdge)
{
Type5.setDir(-1);
for (int count = 0; count < 11; count++)
{
invaderMoveInstance5.Play();
Type5Invaders[count].MoveY(8);
}
}
}
else
{
//this means that the invader does not exist anymore, so we do nothing
}

Categories