I'm currently working on a MCTS implementation for a simple board game.
I think I almost got it right, but whenever I want to iterate more than 1 time
(time or number(i) as factor for iterations) the game freezes. I would be glad if someone has a solid idea about what the issue could be.
// method to find best turn
Node findBest( )
{
Node startingPoint = new Node();
startingPoint.CurrentField_Boxes = GameBoard.boxs;
startingPoint.CurrentField_H = GameBoard.horizontal_line;
startingPoint.CurrentField_V = GameBoard.vertical_line;
Node rootNode = startingPoint;
for( int i = 0; i < 2; i++)
{
//Selection
Node promisingNode = selectPromisingNode(rootNode);
Debug.Log("POST SELECTION CHECK VON X/Y " + promisingNode.x + "/" + promisingNode.y);
//Expansion
if (!checkField())
{
expandNode(promisingNode);
}
//Simulation
Node nodeToExplore = promisingNode;
Debug.Log("POST 2 SELECTION CHECK VON X/Y " + nodeToExplore.x + "/" + nodeToExplore.y);
if (promisingNode.getChildren().Count > 0)
{
//get random child node
nodeToExplore = promisingNode.getRandomChild();
Debug.Log("POST 3 SELECTION CHECK VON X/Y " + nodeToExplore.x + "/" + nodeToExplore.y);
}
int playoutResult = simulation(nodeToExplore);
Debug.Log("SIM RESULT :" + playoutResult);
//backpropagation
backPropogation(nodeToExplore);
}
Debug.Log("*********************************");
Node bestNode = new Node();
Debug.Log("POST 4 SELECTION CHECK VON X/Y " + rootNode.getBestChild().x + "/" + rootNode.getBestChild().y);
bestNode = rootNode.getBestChild();
return bestNode;
}
private Node selectPromisingNode(Node rootNode)
{
Debug.Log("SELECTING");
Node node = new Node();
node = rootNode;
while (node.getChildren().Count != 0) // checking if terminal
{
findBestNodeWithUCT(node);
}
return node;
}
Node findBestNodeWithUCT( Node root)
{
double j = 0;
int bestIndex = 0; ;
for (int i = 0; i < root.getChildren().Count; i++)
{
if (root.getChildren()[i].getVisitTimes() / root.getChildren()[i].getResult() > j)
{
j = root.getChildren()[i].getVisitTimes() / root.getChildren()[i].getResult();
bestIndex = i;
Debug.Log("BEST INDEX : " + bestIndex);
}
}
Node bestOption = root.getChildren()[bestIndex];
Debug.Log("BEST OPTION RETURN X/Y : " + bestOption.x + "/" + bestOption.y);
return bestOption;
}
private void expandNode(Node node)
{
Debug.Log("Start EXPANSION");
bool vert = false;
bool hori = false;
//Phase I : creating new Node
//create a new node wit certain action A
Node newNode = new Node();
//init and setting parent
newNode.parent[0] = node;
//init gamefield into node
newNode.CurrentField_V = node.CurrentField_V;
newNode.CurrentField_H = node.CurrentField_H;
//init and setting child parent relationship
newNode.visitTimes++;
Debug.Log("ERSTELLE NEUEN KNOTEN");
//Phase II : Validating Action
//create a action a which leads to the expansion
int x = UnityEngine.Random.Range(0,4);
int y = UnityEngine.Random.Range(0, 4);
//checking variables for valid coordinates
Debug.Log("AKTION A IN EXPANSION :" + x + y);
//Prechecking of generated coordinates to set corresponding obj
if (y < 3)
{
vert = true;
}
if (x < 3 )
{
hori = true;
}
else if (x == 3 && y == 3)
{
Debug.Log(" OUT OF BOUNDS :NEW RANDOM");
expandNode(node);
}
//Phase III : Doing Action
if(vert && newNode.CurrentField_V[x, y].tag == "is play" && hori && newNode.CurrentField_H[x, y].tag != "is play")
{
expandNode(node);
}
//setting choice into sim gamefield with green color
if (vert && newNode.CurrentField_V[x, y].tag != "is play")// && GameBoard.sim_vertical_line[x, y].tag != "is play")
{
Debug.Log("SETZEN DER VERTIKALEN AKTION IN KNOTENSTATE");
GameBoard.sim_vertical_line[x, y].tag = "is play";
GameBoard.sim_vertical_line[x, y].GetComponent<SpriteRenderer>().sprite = greenV;
// newNode.CurrentField_V[x, y].tag = "is play";
newNode.x = x;
newNode.y = y;
node.nodeChildren.Add(newNode);
}
if (hori && newNode.CurrentField_H[x, y].tag != "is play" )//&& GameBoard.sim_horizontal_line[x, y].tag != "is play")
{
Debug.Log("SETZEN DER HORIZONTALEN AKTION IN KNOTENSTATE");
GameBoard.sim_horizontal_line[x, y].tag = "is play";
GameBoard.sim_horizontal_line[x, y].GetComponent<SpriteRenderer>().sprite = greenH;
// newNode.CurrentField_H[x, y].tag = "is play";
newNode.x = x;
newNode.y = y;
node.nodeChildren.Add(newNode);
}
else { expandNode(node); }
}
private void backPropogation(Node nodeToExplore)
{
Node tempNode = nodeToExplore;
Debug.Log("BACKPROPAGATION");
while (tempNode != null)
{
int i = 0;
if (i % 2 == 0)
{
tempNode.visitTimes++;
int j = tempNode.result;
tempNode.setResult(j);
tempNode = tempNode.getParent();
}
else if (i % 2 != 0)
{
tempNode.visitTimes++;
int j = tempNode.result;
tempNode.setResult(j * -1);
tempNode = tempNode.getParent();
}
i++;
}
}
One iteration works fine and I already got a better behaviour than on a random bot, but with only about 5-6 nodes max can get created in one iteration.
Thanks a lot.
In the selectPromisingNode() function, you have an infinite loop starting from your second iteration. You start out plugging the root node into that function. Starting from the second iteration, the root node has more than 0 children, so the condition of the while-loop is satisfied. Inside the loop, you do not change the value of the node variable, so you'll infinitely often keep calling findBestNodeWithUCT() with the root node as argument.
You'll probably want to change that line of code inside the loop to: node = findBestNodeWithUCT(node), so that you actually start traversing the tree a bit instead of staying stuck at the root.
I didn't check the rest of the code, so I do not know if there are more errors, but this at least seems to precisely explain the problem you're experiencing
Related
I've been smashing my head against this problem for days and have tried a tons of different things. I've been all over the forums, tried everything I've seen with no luck. My issue could be that I don't have an override, but I can't figure out how to get that to work.
I want to check if an array of 5,000+ elements contains a user-entered word. The word gets entered character by character and combined into a string(guessString). And then I use .Contains() to see if that word is in an array.
***EDIT please see screenshots for debug logs WordArray Elements -- Debug Output -- Debug With whitespace detection -- Code that doesnt work
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Linq;
public class WordAction : MonoBehaviour
{
TMP_Text m_textComponent;
TMP_Text m_currentSquare;
public TMP_Text[] squareArray;
List<string> dupKey = new List<string>();
public string[] WordArray;
public List<string> DictionaryList = new List<string>();
public TextAsset file;
[SerializeField]
Color correctColor, wrongColor, maybeColor;
[SerializeField]
float colorFadeSpeed, colorFadeTime; // 2f, 1f
public float waitTime;
public string levelID;
public string key;
public AudioSource revealSFX;
bool guess;
string guessString;
int guessRegulator;
int guessCount = 1;
int lessGuessCount; // Starts variable at value of first current row element
int maxGuessCount;
string[] guessStringArray = new string[1];
void Start()
{
for (int i = 0; i < 5; i++) // Duplicate key
{
dupKey.Add(key[i].ToString());
}
var content = file.text;
string[] AllWords = content.Split('\n');
WordArray = AllWords;
}
public void Magic()
{
StartCoroutine(CompareKey());
}
IEnumerator CompareKey()
{
guessRegulator++;
GuessRegulatorFunction();
lessGuessCount = (guessCount * 5) - 5; // Starts variable at value of first current row element
maxGuessCount = guessCount * 5;
guessCount++; // Moves to next row
int k = 0; // Indexer for key[]
int cW = 0; // Indexer for CombineWord()
GameObject keyGO; // Keyboard GO
for (int i = lessGuessCount; i < maxGuessCount; i++)
{
if (cW < 1)
{
CombineWord(i);
cW++;
}
bool match = WordArray.Contains(guessString); // not working
Debug.Log(match);
if (match)
{
//do stuff
}
//compare stuff
string guessStr = squareArray[i].text.ToString();
string keyStr = key[k].ToString();
bool result = guessStr == keyStr;
if (!result && !dupKey.Contains(guessStr))
{
//wrong stuff
GameObject parentGO = squareArray[i].transform.parent.gameObject; // Gets parent of SquareArray element
Image parentImage = parentGO.GetComponent<Image>(); // Gets Image component of parent game object
keyGO = GameObject.Find(squareArray[i].text); // Keyboard
Image keyParentImage = keyGO.GetComponent<Image>(); // Keyboard
wrongColor.a = 255;
keyParentImage.color = wrongColor;
parentImage.color = wrongColor;
yield return null;
}
if (result)
{
//correct stuff
dupKey[k] = "";
GameObject parentGO = squareArray[i].transform.parent.gameObject; // Gets parent of SquareArray element
Image parentImage = parentGO.GetComponent<Image>(); // Gets Image component of parent game object
keyGO = GameObject.Find(squareArray[i].text); // Keyboard
Image keyParentImage = keyGO.GetComponent<Image>(); // Keyboard
correctColor.a = 255;
keyParentImage.color = correctColor;
parentImage.color = correctColor;
yield return null;
}
if (!result && dupKey.Contains(guessStr))
{
//yellow stuff
for (int x = 0; x < 5; x++) // Duplicate key
{
if (guessStr == dupKey[x])
{
dupKey[x] = "";
}
}
GameObject parentGO = squareArray[i].transform.parent.gameObject; // Gets parent of SquareArray element
Image parentImage = parentGO.GetComponent<Image>(); // Gets Image component of parent game object
keyGO = GameObject.Find(squareArray[i].text); // Keyboard
Image keyParentImage = keyGO.GetComponent<Image>(); // Keyboard
maybeColor.a = 255;
keyParentImage.color = maybeColor;
parentImage.color = maybeColor;
yield return null;
}
revealSFX.Play();
k++;
yield return new WaitForSeconds(waitTime);
}
dupKey.Clear();
for (int i = 0; i < 5; i++) // Duplicate key
{
dupKey.Add(key[i].ToString());
}
}
void GuessRegulatorFunction()
{
guessRegulator++; // Stops multiple guess attempts
for (int i = 0; i < (guessCount * 5); i++) // Checks if row is blank when guessing
{
if (squareArray[i].text == "")
{
guess = false;
guessRegulator = 0; // Resets guess regulator
break;
}
else
{
guess = true;
}
}
if (guessRegulator > 1 || guess == false) // Stops multiple guess attempts
{
return;
}
}
public void BackSpace()
{
for (int i = guessCount * 5; i > (guessCount * 5) - 6; i--)
{
if (squareArray[i].text != "")
{
squareArray[i].text = "";
break;
}
}
}
public void InputLetter()
{
guessRegulator = 0;
for (int i = 0; i < guessCount * 5; i++)
{
if (squareArray[i].text == "")
{
squareArray[i].text = EventSystem.current.currentSelectedGameObject.name.ToString();
break;
}
}
}
void CombineWord(int i)
{
var string1 = squareArray[i].text.ToString();
var string2 = squareArray[i + 1].text.ToString();
var string3 = squareArray[i + 2].text.ToString();
var string4 = squareArray[i + 3].text.ToString();
var string5 = squareArray[i + 4].text.ToString();
guessString = string1 + string2 + string3 + string4 + string5;
//Debug.Log(guessString);
}
}
I've taken your line of code that isn't working and copied it verbatim. I've then taken the data that you say is in the WordArray and guessString variables and set those up. Then I ran this:
var WordArray = new [] { "WHICH", "THERE", "THEIR", "ABOUT" };
var guessString= "THERE";
bool match = WordArray.Contains(guessString);
Console.WriteLine(match);
match comes out True.
Your variables do not contain the data you think they do.
It's likely that the content that you call .Split('\n') on actually contains Windows end of line markers, so a combination of "\r\n". Since you only split on '\n' it's likely that the '\r' remains in your strings and hence "THERE" does not match "THERE\r".
Try this instead:
.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
If your code is run on either Windows or on Linux the above line works. Just watch out for files that mix the endings.
Well, your WordArray is always empty. You put your file content into a local variable called AllWords.
Your word will never be found in an empty array.
I am trying to make a minesweeper game and i have added a button to generate a new "map". the problem is that the tiles can't tell how many mines are beside them after generating a new map (they can tell it on the initial generation).
Here is my code for generating a map:
void GenerateMines()
{
RowCount = 1;
ColumnCount = 1;
tempNumber = 0;
IdSet = 0;
//Generates random numbers equal to the amount of mines and adds it to the MineIds_ list
for (int i = 0; i < AmountOfMines; i++)
{
do
{
tempNumber = Random.Range(0, AmountOfTiles);
}
while (MineIds_.Contains(tempNumber));
{
MineIds_.Add(tempNumber);
}
}
//Instantiates all mines and puts a mine on the ones that are listed in the MineIds_ list, it also names the tiles according to which row and column they are in.
for (int i = 0; i < AmountOfTiles; i++)
{
GameObject tile_ = Instantiate(tile, transform);
tile_.GetComponent<tileProperties>().id = IdSet;
tile_.GetComponent<tileProperties>().RowNumber = RowCount;
tile_.GetComponent<tileProperties>().ColumnNumber = ColumnCount;
tile_.name = RowCount + " " + ColumnCount;
//mine assigning
if (MineIds_.Contains(IdSet))
{
tile_.GetComponent<tileProperties>().hasMine = true;
}
IdSet++;
ColumnCount++;
if (GetComponent<GridLayoutGroup>().constraintCount < ColumnCount)
{
ColumnCount = 1;
RowCount++;
}
}
//tells the tiles with mines on them to add to the adjacentMines counter on the tiles beside them
RowCount = 1;
ColumnCount = 1;
for (int i = 0; i < AmountOfTiles; i++)
{
GameObject currentTile = GameObject.Find(RowCount + " " + ColumnCount);
if (currentTile.GetComponent<tileProperties>().hasMine == true)
{
SendMineData();
}
ColumnCount++;
if (GetComponent<GridLayoutGroup>().constraintCount < ColumnCount)
{
ColumnCount = 1;
RowCount++;
}
}
}
And here is the code I use to set the AdjacentMines counter for each tile:
//this is only triggered if the tile has a mine on it, it finds the 8 tiles next to it and incerases their AdjacentMines counter by 1
public void SendMineData()
{
int ColumnPointer;
int RowPointer;
ColumnPointer = ColumnCount;
RowPointer = RowCount;
ColumnPointer--;
GameObject tileOnPointer = GameObject.Find(RowPointer + " " + ColumnPointer);
if (tileOnPointer != null)
{
tileOnPointer.GetComponent<tileProperties>().AdjacentMines++;
}
RowPointer--;
GameObject tileOnPointer2 = GameObject.Find(RowPointer + " " + ColumnPointer);
if (tileOnPointer2 != null)
{
tileOnPointer2.GetComponent<tileProperties>().AdjacentMines++;
}
ColumnPointer++;
GameObject tileOnPointer3 = GameObject.Find(RowPointer + " " + ColumnPointer);
if (tileOnPointer3 != null)
{
tileOnPointer3.GetComponent<tileProperties>().AdjacentMines++;
}
ColumnPointer++;
GameObject tileOnPointer4 = GameObject.Find(RowPointer + " " + ColumnPointer);
if (tileOnPointer4 != null)
{
tileOnPointer4.GetComponent<tileProperties>().AdjacentMines++;
}
RowPointer++;
GameObject tileOnPointer5 = GameObject.Find(RowPointer + " " + ColumnPointer);
if (tileOnPointer5 != null)
{
tileOnPointer5.GetComponent<tileProperties>().AdjacentMines++;
}
RowPointer++;
GameObject tileOnPointer6 = GameObject.Find(RowPointer + " " + ColumnPointer);
if (tileOnPointer6 != null)
{
tileOnPointer6.GetComponent<tileProperties>().AdjacentMines++;
}
ColumnPointer--;
GameObject tileOnPointer7 = GameObject.Find(RowPointer + " " + ColumnPointer);
if (tileOnPointer7 != null)
{
tileOnPointer7.GetComponent<tileProperties>().AdjacentMines++;
}
ColumnPointer--;
GameObject tileOnPointer8 = GameObject.Find(RowPointer + " " + ColumnPointer);
if (tileOnPointer8 != null)
{
tileOnPointer8.GetComponent<tileProperties>().AdjacentMines++;
}
}
And Here is tho code i use for when the "generate new map" button is pressed:
public void GenerateNewMap()
{
// Destroys all tiles
foreach (Transform child in map.transform)
{
Destroy(child.gameObject);
}
MineIds_.Clear();
//Generates new set of tiles
GenerateMines();
}
The problem is the following: If i press the "Generate new map button" the tiles will generate normally but the adjacentMines counter will be set to 0 on each tile. But when i Delete all the tiles manually from the hierarchy during runtime and then press the button it all works perfectly.
In C#, you're not supposed to modify a collection when using a foreach loop.
Use a for loop instead. Further, the Destroy doesn't happen immediately.
I'm not quite sure of the timing, but it takes at least one frame before the Destroy starts processing. So the loop you invoke Destroy happens say in frame 0, while the destruction actually starts happening in frames starting at 1, and may not complete within the next frame. At least that's been my experience.
There should not be a difference, the bug likely does not come from this.
The Destroy method is not immediate! That might be related. There is a delay between the call and the actual destruction, which might not exist when deleting in the scene. This could cause a timing issue with the current tiles being destroyed after the new ones are created.
You are also using GameObject.Find (which btw is slow) to find neighbors, but this technique is dependant on the scene names etc. which might be also related.
What you could do is store a bi-dimensional array of GameObject references as the map. Each tile can then access this array to know what objects are its neighbors. You can populate the array quite easily in your GenerateMines method, and clean it when you destroy the objects.
I'm in the process of making a 2D tile map and i'm now trying to implement A* pathfinding. I'm following the Wikipedia pseudocode for A*.
Things are going quite well except for some weird behaviour in the decisions taken by the algorithm.
My code so far:
void Pathfinding(Point from, Point destination) {
goalNode = new Node(destination, 0, 0);
startNode = new Node(from, 0, ManhattanDistance(from, destination));
open = new List<Node>(); //list of nodes
closed = new List<Node>();
open.Add(startNode); //Add starting point
while(open.Count > 0) {
node = getBestNode(); //Get node with lowest F value
if(node.position == goalNode.position) {
Debug.Log("Goal reached");
getPath(node);
break;
}
removeNode(node, open);
closed.Add(node);
List<Node> neighbors = getNeighbors(node);
foreach(Node n in neighbors) {
float g_score = node.G + 1;
float h_score = ManhattanDistance(n.position, goalNode.position);
float f_score = g_score + h_score;
if(isValueInList(n, closed) && f_score >= n.F)
continue;
if(!isValueInList(n, open) || f_score < n.F) {
n.parent = node;
n.G = g_score;
n.G = h_score;
if(!isValueInList(n, open)) {
map_data[n.position.x, n.position.y] = 4;
open.Add(n);
}
}
}
}
}
The result of running this code:
Blue is the nodes from the open list and green is the path chosen to the goal node.
SOLUTION:
void Pathfinding(Point from, Point destination) {
goalNode = new Node(destination, 0, 0);
startNode = new Node(from, 0, ManhattanDistance(from, destination));
open = new List<Node>(); //list of nodes
closed = new List<Node>();
open.Add(startNode); //Add starting point
while(open.Count > 0) {
node = getBestNode(); //Get node with lowest F value
if(node.position == goalNode.position) {
Debug.Log("Goal reached");
getPath(node);
break;
}
removeNode(node, open);
closed.Add(node);
List<Node> neighbors = getNeighbors(node);
foreach(Node n in neighbors) {
float g_score = node.G + 1;
float h_score = ManhattanDistance(n.position, goalNode.position);
float f_score = g_score + h_score;
if(isValueInList(n, closed) && f_score >= n.F)
continue;
if(!isValueInList(n, open) || f_score < n.F) {
n.parent = node;
n.G = g_score;
n.H = h_score;
if(!isValueInList(n, open)) {
map_data[n.position.x, n.position.y] = 4;
open.Add(n);
}
}
}
}
}
First, your open Nodes should be sorted in descending order, while in your code - there is no ordering. You compute the distance (g) and the heuristics (h) but never actually use it. You should consider using ordered container instead of lists (as sorting lists in each iteration won't be efficient)
Second, you do not store the heuristic value in the node as
n.G = h_score;
should be
n.H = h_score;
I've an issue to ask you guys.
I have a class shown below:
public class Node
{
public int Kova1; // Kova 1
public int Kova2; // Kova 2
public int Kova3; // Kova 3
public int ActionNo; // Yapılan İşlem
public Node(int kova1, int kova2, int kova3, int actionNumber)
{
Kova1 = kova1;
Kova2 = kova2;
Kova3 = kova3;
ActionNo = actionNumber;
}
public Node(int kova1, int kova2, int kova3)
{
Kova1 = kova1;
Kova2 = kova2;
Kova3 = kova3;
}
public Node()
{
}
public Node AnneNode;
}
And these functions:
public void CocukNodeOlustur(LinkedList<Node> Acik, LinkedList<Node> Kapali, Node temp)
{
Node cocukState;
Node temp2 = temp;
for (int i = 0; i < 12; i++)
{
cocukState = YeniStateOlustur(temp, i);
if ((ActionKontrol(cocukState)) && (GoalBulundu(Acik, Kapali, cocukState)) &&
((cocukState.Kova1 != temp2.Kova1) && (cocukState.Kova2 != temp2.Kova2) && (cocukState.Kova3 != temp2.Kova3)))
{
cocukState.AnneNode = temp;
Acik.AddFirst(temp);
}
}
}
public Node YeniStateOlustur(Node s, int j)
{
int tempKova1, tempKova2, tempKova3;
Node yeniCocuk = new Node();
yeniCocuk = s;
yeniCocuk.ActionNo = j;
// Gelen numaraya göre uygulanan işlemin seçimi yapılıyor.
switch (j)
{
case 0:
{
yeniCocuk.Kova1 += (3 - yeniCocuk.Kova1);
yeniCocuk.Kova2 += 0;
yeniCocuk.Kova3 += 0;
}
break;
case 1:
{
yeniCocuk.Kova1 += 0;
yeniCocuk.Kova2 += (5 - yeniCocuk.Kova2);
yeniCocuk.Kova3 += 0;
}
break;
}
return yeniCocuk;
}
In the main function
Node temp = new Node();
while (!(Acik.Count == 0))
{
p.CocukNodeOlustur(Acik, Kapali, temp);
Kapali.AddLast(temp);
}
So When I debug my program, I see that Whenever the code jumps to the YeniStateOlustur() function, all the Node instance's in program is affected by the changes in YeniStateOlustur(). It seems the instance in the function overwrite all instances of Node class.
I don't understand why it happens?
How can I overcome this?
My best regards and sory for the long post.
The problem is that all of the nodes are the same instance. Your sample code includes "new Node()" only twice, and in the second case (inside the method YeniStateOlustur), the new instance is immediately discarded. That function therefore returns the same node that was passed to it:
public Node YeniStateOlustur(Node s, int j)
{
int tempKova1, tempKova2, tempKova3;
Node yeniCocuk = new Node();
yeniCocuk = s;
//...
return yeniCocuk;
}
In the method CocukNodeOlustur, all node variables point to the same Node:
public void CocukNodeOlustur(LinkedList<Node> Acik, LinkedList<Node> Kapali, Node temp)
{
// here, temp == temp
Node cocukState;
// now, temp == temp and cocukState is uninitialized.
Node temp2 = temp;
// now, temp == temp, temp2 == temp, and cocukState is uninitialized.
for (int i = 0; i < 12; i++)
{
cocukState = YeniStateOlustur(temp, i);
// now, temp == temp, temp2 == temp, and cocukState == temp
if ((ActionKontrol(cocukState)) && (GoalBulundu(Acik, Kapali, cocukState)) &&
((cocukState.Kova1 != temp2.Kova1) && (cocukState.Kova2 != temp2.Kova2) && (cocukState.Kova3 != temp2.Kova3)))
{
cocukState.AnneNode = temp;
Acik.AddFirst(temp);
}
}
}
Your code seems to assume that Node is a value type (struct), but it is obviously a reference type (class). If you're unsure about the difference, you should take a step back and do some reading and experimentation.
A quick fix might be to change the declaration of Node to a struct, but I would recommend against that. Programming with structs can be very tricky, and that would be especially true if your understanding of the differences between structs and classes is shaky.
public void HeightIterative()
{
int counter = 0;
int counter2 = 0;
TreeNode current=root;
if(current != null)
{
while(current.LeftNode!=null)
{
counter++;
current = current.LeftNode;
}
while(current.RightNode!=null)
{
counter2++;
current = current.RightNode;
}
}
int res = 1+Math.Max(counter, counter2);
Console.WriteLine("The Height Of Tree Is: "+res);
}
I wrote iterative method, to calculate height of tree. but in some cases its not working properly. As in case:
10
1
2
3
4
5
18
17
16
15
14
13
what's the problem. according to this sequence height of tree is 6 where as my code is showing 5.
You are using two loops, but each loop investigated only oneside of node, but each node in tree has two sides you should investigate it all. You can do it through recursion call.
private int GetLen(TreeNode node)
{
var result = 0;
if(node != null)
{
result = Math.Max(GetLen(node.LeftNode), GetLen(node.RightNode)) + 1;
}
return result;
}
public void HeightIterative()
{
int res = GetLen(root);
Console.WriteLine("The Height Of Tree Is: "+res);
}
Iterative version:
private class NodeInfo
{
public NodeInfo(TreeNode node, int len)
{
Node = node;
Len = len;
}
public TreeNode Node {get; private set;}
public int Len {get; private set;}
}
public void HeightIterative()
{
int maxLen = 0;
var queue = new Queue<NodeInfo>();
queue.Enqueue(new NodeInfo(root, 1));
while (queue.Count > 0)
{
var item = queue.Dequeue();
var current = item.Node;
var currentLen = item.Len;
if (current.LeftNode != null)
{
queue.Enqueue(new NodeInfo(current.LeftNode, currentLen + 1));
}
if (current.RightNode != null)
{
queue.Enqueue(new NodeInfo(current.RightNode, currentLen + 1));
}
if (currentLen > maxLen)
{
maxLen = currentLen;
}
}
Console.WriteLine("The Height Of Tree Is: " + maxLen);
}
There is a way that does not require any extra space except the queue for storing the nodes.
Add child nodes of a current element and remember size of the queue.
Let each dequeue call decrement the counter
When counter reaches zero that means we are done with current level.
Repeat and count number of times counter reaches zero - this is the depth/height of the tree
Code goes like this :
public int treeDepth(Node root){
int height = 0;
int counterNodesInLevel = 1;
if(root!=null)
{
Queue<Node> queue=new Queue<Node>()
queue.enqueue(root);
while (!queue.isEmpty()){
Node current = queue.dequeue();
counterNodesInLevel -= 1;
if(current.left!=null){
queue.enqueue(current.left)
}
if(current.right!=null){
queue.enqueue(current.right)
}
if (counterNodesInLevel == 0){
height += 1;
counterNodesInLevel = queue.Size();
}
}
}
return height;
}
Time complexity is O(N), space complexity is O(N)
The problem:
You are finding the depth of the left-most node in the first loop, and the right-most in the second, and never interrogating any node that involves going down to the left AND the right.
A solution:
Have a single loop that drills down the left nodes, but adds each right node that it 'skips' into a queue. When you run out of left nodes, pop-off a node form your queue and continue on until the queue becomes empty. You'll need to store the height of each node you put in the queue with that node.
public int Height()
{
int result = GetMaxHeight(this.Root,0);
return result;
}
private int GetMaxHeight(Node<T> node,int count)
{
int leftMax = 0, rightMax = 0;
if (node.Left != null)
{
leftMax = GetMaxHeight(node.Left, count+1);
}
if (node.Right != null)
{
rightMax = GetMaxHeight(node.Right, count + 1);
}
if(node.Left==null && node.Right == null)
{
return count;
}
return Math.Max(leftMax,rightMax);
}