dealing with lists inside of types "help needed"

BlitzMax Forums/BlitzMax Beginners Area/dealing with lists inside of types "help needed"

Xin(Posted 2011) [#1]
hey guys i have sort of a problem. i was trying to make a small pathfinding demo to use for my game. and i cant progress to writing the actual pathfinding code because im stuck coding the nodes.

its structured like this. each node has a neighbor list. which is a Tlist that contains all of its neighboring nodes. the nodes also have 2 methods which are add neighbor and remove neighbor.

Method addneighbor(N:node) 'add a neighboring node

Listaddlast(Self.neighborlist,N)
Listaddlast(N.neighborlist,Self)

End Method


Method removeneighbor(N:node) 'remove a neighboring node

ListRemove(Self.neighborlist,N)
ListRemove(N.neighborlist,Self)

End Method

i also have a destroy method that removes the node from the global entitylist and removes all neighbors.

Method destroy()

For Local N:node=EachIn Self.neighborlist

Self.removeneighbor(N)

Next

RemoveLink(Self.link) ' deletes itself from the global list

End Method


now heres the problem. in the node type's drawing method i have a for loop that iterates through a node's neighborlist draws a line to each of the node's neighbors shown here.


Method draw1()

For Local N:node=EachIn Self.neighborlist

DrawLine(Self.x,Self.y,N.x,N.y)

Next


End Method


even though a neighbor is dead. i still see lines drawn to it. is this due to bad programming or is this a blitz max bug. I've tried for almost 2 weeks to get this to work. so please any help would be appreciated. i feel as if i'm going to rip out my hair lol

Last edited 2011


matibee(Posted 2011) [#2]
I'm not sure without testing but could calling ListRemove (during removeneighbor) invalidate the iterator 'N:node' that's being used during destroy() ?

I'll run some tests if I get chance.


col(Posted 2011) [#3]
Hiya,

This can be a common problem with cyclic algorithms.

Although initially looking at what you shown here, it should work ok with adding and deleting in the neighborlist. However you may find that because you are using the 'Self' instance to add ITSELF to a neighbors list AS WELL as adding that same neighbor to ITSELFs list, then when you process that neighbor looking for neighbors again, the same thing will happen, so you'll end up with dual entries in the neightborlist. This is more than likely what is causing entries in the neightborlist even after a neighbor is removed.

There are several ways around this problem. One of the simplest is to track in the list whether the neighbor is already in the neighborlist, and if it is then don't to add it in again.

Just a note :- you can use [ code ] Place code here [ /code ] or [ codebox ] Place code here [ /codebox ] to post code. EDIT:- with the spaces removed between the square brackets and the words code /code codebox /codebox.

If you need more help, then please post a complete working example which demonstrates your issue, so we can find the exact cause.

Have fun.

Last edited 2011


Xin(Posted 2011) [#4]
i can see where the problem is. its not the case of having duplicates. i added a method that prints the ID numbers of all the nodes in its neighborlist. and from what i see, the problem is in the Delete() method. it seems that its removing the wrong nodes. instead of removing itself from the neighbors neighborlist, its removing other nodes instead of itself. i have no idea why this is happening. its as if its accessing the wrong memory address or something. thats the only thing i can think of at the moment

Last edited 2011


col(Posted 2011) [#5]
If you want to post a smaller version of your code, I'm sure we can help sort this out for you.


Jesse(Posted 2011) [#6]
Are you passing a sort function into the list?
that has a know bug when removing objects from the list.

the only way we can really help is if you post working code.

Last edited 2011


Xin(Posted 2011) [#7]
ok here you go. i made a smaller version of my program for you guys to look at.

 

SuperStrict

Global entitylist:TList=New TList'---------------------------------global entitylist---------------------------





'--------------------------------------------------main function "gameloop"------------------------------------------
Function gameloop(list:TList)
	SortList(entitylist)
	For Local A:entity= EachIn list

		A.preprocess()
	Next

	For Local X:entity= EachIn list

		X.update()
	Next

	For Local Y:entity= EachIn list

		Y.draw1()
	Next

	For Local Z:entity= EachIn list

		Z.draw2()
	Next

	For Local P:entity= EachIn list

		P.draw3()
	Next
End Function

Function distance:Double(x1:Double,y1:Double,x2:Double,y2:Double)  '----- gets the distance between two points     
	Local deltax:Double=x2-x1
	Local deltay:Double=y2-y1
	Local calculation:Double=((deltax*deltax)+(deltay*deltay))
	Return Sqr(calculation)
End Function



Type entity  '--------------base class for everything that exists in the gameworld---------------------------------

	Field alive:Byte=True
	Field x:Double
	Field y:Double
	Field zdepth:Int
	Field link:TLink
	Field xprevious:Double
	Field yprevious:Double

	Method compare:Int(other:Object)
		
		If(entity(other).zdepth<Self.zdepth)
		
			Return 1
		
		Else If(entity(other).zdepth>Self.zdepth)
		
			Return -1
		
		Else
		
			Return 0
		
		End If
		
	End Method
	
	
	Method preprocess() Abstract' make small changes before the data is updated

	Method update() Abstract' updates game data
	
	Method draw1() Abstract' draws the lines
	
	Method draw2() Abstract' draws the oval nodes
	
	Method draw3() Abstract' draws the text over the nodes
	

End Type

'------------------------------------------------------------------------------------------NODE CLASS-----------------------------------------------------------------------------------------'

Type node Extends entity

	Field neighborlist:TList= New TList
	Field id:Int
	Field mouseclose:Byte=False
	
	Method addneighbor(A:node)  'add a neighbor node

		ListAddLast(Self.neighborlist,A)
		ListAddLast(A.neighborlist,Self)
	

	End Method

	Method removeneighbor(N:node)   'remove a neighboring node
		
		ListRemove(Self.neighborlist,N)
	    ListRemove(N.neighborlist,Self)
		
	End Method
	
	
	Method destroy() 'destroys the node and deletes all connections to other nodes
	
		For Local No:node=EachIn Self.neighborlist
		
			Self.removeneighbor(No)
		
		Next
	
		ClearList(Self.neighborlist)
		
		RemoveLink(Self.link)

	End Method
	
	Function Create:node(x:Double,y:Double) 'creates a node and returns it
	
	Local N:node=New node
	
		N.x=x
		N.y=y
		N.id=Rand(0,9999)
		N.link=ListAddLast(entitylist,N)
		
		Return N
	
	End Function


	Method preprocess()
	
		If(distance(Self.x,Self.y,MouseX(),MouseY())<30)  'this bit of code tells the node if the mouse is hovering above it'
		
			Self.mouseclose=True
			mousefree=False
			
		Else
			
			Self.mouseclose=False
		
		End If
	
	End Method
	
	Method update()
	End Method

	Method draw1()' this code tells the node to draw a line to each of its neighbors '
	
		For Local DR:node=EachIn Self.neighborlist
		
			DrawLine(Self.x,Self.y,DR.x,DR.y)
	
		Next
	

	End Method 
	
	Method draw2()' this draws the node itself OVER the lines'
	
		
		SetColor(100,100,100)
			
		DrawOval(Self.x-20,Self.y-20,40,40)
			
		SetColor(255,255,0)
			   
	End Method 
	
	Method draw3() 'draws its own id and all the neighbors its connected to 
		DrawText(Self.id,Self.x-20,Self.y-30)
		
		
		Local count:Int=0
		For Local N:node=EachIn Self.neighborlist
		
			DrawText(N.id,Self.x-10+count,Self.y)
			count:+40
		Next
	End Method 
	
End Type







SeedRnd(MilliSecs())
'---------------------------------MAIN LOOP----------------------------'
Global mousefree:Byte=True    'true if the mouse cursor isnt hovering above another node

Global MOUSE_LEFT1:Byte=MouseHit(1)    'mouse button variables
Global MOUSE_LEFT2:Byte=MouseDown(1)
Global MOUSE_RIGHT1:Byte=MouseHit(2)
Global MOUSE_RIGHT2:Byte=MouseDown(2)

Graphics(800,600)                    'create the window

Local A:node=node.Create(400,300)    'creates 6 nodes'
Local B:node=node.Create(100,500)
Local C:node=node.Create(700,400)
Local D:node=node.Create(600,100)
Local E:node=node.Create(200,200)
Local F:node=node.Create(Rand(0,800),Rand(0,600))

A.addneighbor(B)     'here i randomly add and then remove neighbors as a test to see where the problem is
A.addneighbor(C)
A.addneighbor(D)
A.addneighbor(E)
A.addneighbor(F)

B.addneighbor(C)
C.addneighbor(D)
D.addneighbor(E)
E.addneighbor(B)

A.removeneighbor(B)
A.removeneighbor(C)
A.removeneighbor(D)
A.removeneighbor(E)
A.removeneighbor(F)

B.removeneighbor(C)
C.removeneighbor(D)
D.removeneighbor(E)
E.removeneighbor(B)

F.addneighbor(A)
F.addneighbor(B)
F.addneighbor(C)
F.addneighbor(D)
F.addneighbor(E)



While Not(KeyDown(KEY_ESCAPE) Or AppTerminate())

	MOUSE_LEFT1:Byte=MouseHit(1)
	MOUSE_LEFT2:Byte=MouseDown(1)
	MOUSE_RIGHT1:Byte=MouseHit(2)
	MOUSE_RIGHT2:Byte=MouseDown(2)
	mousefree=True
	
	Cls
	gameloop(entitylist)    ' the gameloop function i made before'
	
	If(KeyHit(KEY_4))       ' i use these events to destroy a node. this is where i saw the problem
	
	A.destroy()
	
	End If

	If(KeyHit(KEY_5))
	
	F.destroy()
	
	End If
	
	If(KeyHit(KEY_3))
	
	D.destroy()
	
	End If
	Flip
Wend






copy paste and run it and press 3. you will see the problem i am having.


matibee(Posted 2011) [#8]
Looks like it might be a Blitz bug..

Your code only fails to work when Node is derived from TEntity. When you're looking for a derived type in a list it's not finding it.





Jesse(Posted 2011) [#9]
The funny part is that it is finding something. It is removing an object. the first one in the list it seems.


Xin(Posted 2011) [#10]
oooh i see. the code works as long as i dont extend node from the entity type. so does that mean that using lists in derived types is a bad idea?


Jesse(Posted 2011) [#11]
I have used it before and never had that problem. I have a habit of creating my objects as extended types. I am not sure but I think a while back some one had that same problem. It had to be with the level of penetration in the list such as list in list of a list.


Xin(Posted 2011) [#12]
well this is disappointing. this means for every type that contains a list i cant extend it from another type, meaning i have to add more code to process the nodes seperately from everything else. kinda ruins the whole object oriented approach. so there is no way i can do this with extended types?

Last edited 2011


TomToad(Posted 2011) [#13]
i'm not at my computer at the moment, so I can't look this up, but shouldn't removelink(self.link) be entitylist.removelink(self.link) instead?


Xin(Posted 2011) [#14]
no its correct as far as i know. its kinda hard to explain. when you add an object to a list it returns the link. the link is like its exact location in a list. so when you remove it. you're removing it from the list you added it to when you created the link. that's the best way i can explain it lol


col(Posted 2011) [#15]
hmm....

Removing your compare method improves things, with or without using SortList.

Last edited 2011


Xin(Posted 2011) [#16]
its good to know that i can still do what im trying to do but the bad thing is that i have to go and hardcode the update and drawing processes for types with lists as fields within the main gameloop functions. it would be alot easier if i could just extend them from the main entity type because thats what its there for lol.


col(Posted 2011) [#17]
You can still do what you want to do. You just may have to move the compare method to the node type? Not tried it.

EDIT- Just tried it. It didn't work.

I'd personally work on a workaround to remove the need for the compare method by building my own sort. That way you can retain the expected behavior associated with Types and derived types. The compare method gets called in various 'unexpected' places when using TList. It also gets called during the ListRemove functionality, so I'd personally remove this error prone method and build a workaround.

Last edited 2011


Xin(Posted 2011) [#18]
oooh i see.... so its the compare method and sortlist that's causing problems. i just removed them and now it works prefectly. ive never made a function that sorts lists before, but i have made a function that sorted numbers so it shouldn't be that hard to figure out. thanks guys really appreciate the help. id give you all a cookie if i could but sadly technology hasn't progressed to that level yet lol

Last edited 2011


mpmxyz(Posted 2011) [#19]
The problem is that the compare method is used by ListRemove to find your object and you changed its behaviour to compare the objects' zdepth values and not their identities. So you removed a 'random' object with the same zdepth value while using your overwritten version.
You can combine both if you first compare their zdepth value and and then use the compare method of their super type.
That should work:
	Method compare:Int(other:Object)
		If(entity(other).zdepth<Self.zdepth)
			Return 1
		Else If(entity(other).zdepth>Self.zdepth)
			Return -1
		Else 'zdepth values are the same -> it may be the same object
			Return Super.Compare(other) 'using the standard compare method
		End If
	End Method


Last edited 2011


Xin(Posted 2011) [#20]
mpmxyz... you just solved my problem and saved me alot of time. thanks so much! *runs off to code*


TomToad(Posted 2011) [#21]
no its correct as far as i know. its kinda hard to explain. when you add an object to a list it returns the link. the link is like its exact location in a list. so when you remove it. you're removing it from the list you added it to when you created the link. that's the best way i can explain it lol

Yeah, I realized that about 5 minutes after I posted, but I needed to get to work so I couldn't post a correction. Problem with trying to troubleshoot someone's problem while not actually at your computer.

As for the compare method, I don't even use that anymore since it is buggy and will never be fixed. instead, there is a compare function that will work better. Something like this: