Compare and sort have baffled me already...

BlitzMax Forums/BlitzMax Beginners Area/Compare and sort have baffled me already...

Simon S(Posted 2005) [#1]
Okay, so I'm getting to grips with my first Bmax code, and after struggling with the examples on the forum I finally understand what all the fuss is about OO coding.

However I've got completely stuck now. I decided to write a simple robotron style shooter, and things were progressing nicely.

In the following code "being" is my basetype for all things in the game (player, shot and grunt at the moment), So I'd though It'd be nice and easy to override compare method to use the .y variable to z sort all the images

However as soon as I dare override the compare method my delete being code (in workall() setup by killbeing()) completely fails and deletes all manner of random beings.

I though it was due to the list sorting messing up my killlist links, but removing the actual sort command made no difference. It seems the very existance of a new compare method causes my code to fail, without compare ever being called and I can't figure out why.

So I'm obviously unsure of exactly how compare is used. can you guys point me in the right way.

Download the source here:
http://www.zen65317.zen.co.uk/simon/MST3K.zip

and here's a listing of the being code

' make a base type for all creatures
Type being
	' constants for creatures in game
	Const CRhero=1		' CReature hero
	Const CRgrunt=2		' Basic badguy
	Const CRtank=3
	Const SHbasic=50	' basic lazer
	
	Global list:TList			' nblist is a list of pointers to every being to be worked on
	Global killlist:TList		' Anything on this list will get removed from beings

	Field x#,y#,z#
	Field xspd#,yspd#
	Field spd#	' general speed stuff moves
	
	Field image
	Field scalex#,scaley#,alpha#,rot#	' scale, alpha, rotation
	Field blend							'blendtype (ie. ALPHABLEND)
	Field r,g,b							' tint information
	
	Field lastshot,shotdelay	' frames since lastshot and min frames between shots
	Field kind					' Type of being see BNGxxx consts
	
	Function Draw()
		If list=Null Then Return
		For Local b:being=EachIn list
			SetBlend(b.blend) ;	SetScale(b.scalex,b.scaley)
			SetColor(b.r,b.g,b.b) ;	DrawImage b.image,b.x,b.y
		Next
	End Function
	
	Function workall()
		If list=Null Then Return

		' code move everything
		For Local b:being=EachIn list
			b.work()
			' increment the universal timers
			b.lastshot=b.lastshot+1
		Next
		' got their positions? cool now draw em	
		SortList(list,True)
		' collide
		' react	
			
		If killlist<>Null
			For Local b:being=EachIn killlist
				list.remove(b)
			Next
			ClearList(killlist)
		EndIf
	End Function
	
	Method work() Abstract
	
	' overide compare to for use with sortlist
	Method Compare:Int(o:Object)
		Return Sgn(y-being(o).y)
	End Method
	
	' set up master list and default details
	Method initbeing(kind,x,y,image,scx#,scy#)
		If list=Null Then list=New TList
		Self.image=image
		self.x=x ; self.y=y
		self.scalex=scx ; self.scaley=scy
		self.blend=MASKBLEND
		self.r=255 ; self.g=255 ; self.b=255
		self.shotdelay=2
		ListAddLast list,Self
	End Method

	
	Method killbeing()
		If killlist=Null Then killlist=New TList
		ListAddLast killlist,Self
	End Method
	
End Type



Tibit(Posted 2005) [#2]
Test to see if this works. It's how I would use the compare method, but it should work with sign too, like you did it, it seems right.
' page 26
Method Compare(O:Object)'Override Original
   Local B:Being = Being(O)
   If B <> Null
        If B.Y > Y Return 1 Else Return -1
   EndIf
EndMethod
A few code Tips: ( incase you didn't know )
1. In methods you don't need to use self, that is assumed, only required if you need to put that object to another function.
2. b.lastshot=b.lastshot+1 can be written b.lastshot:+1
3. Instead if New TList you can use the command CreateList()
4. Also Sort can be called from the List, List.Sort()
5. Instead if ListAddLast myList,Object you can use myList.AddLast( Object )
6. I recommend writing your draw function as a method like this:
Method Draw()
    SetBlend(blend) ;	SetScale(scalex,scaley)
    SetColor(r, g, b) ;	DrawImage image,x,y
Endmethod
That's just tips, do what you feel is right for you.


Bot Builder(Posted 2005) [#3]
Another tip, all those constants in the beginning of they type are a bad idea. It's like you're defining types of the type, in which case you should use types extending the being type.


Simon S(Posted 2005) [#4]
Thanks for the tips. I'm very familiar with blitz2D/3D, but methods and functions in types are very new ground for me, so any advice is welcome.

The constants I used are a hangover from when I started the code B2D style. Somewhere along the line I decided to throw myself into extended types, which now means I don't need any such nonsense. It will take some adjusting. As I said, OO is entirely new territory for me.

I tried your alternate compare Wave, but now the delete list fails to get rid of anything, so the bullets now hang about in the memory instead.

I'll take another look at it in the morning. The thing that's really confusing me is how compare can affect my delete code without ever being called. Is there some other basic commands that rely upon the compare function?


Tibit(Posted 2005) [#5]
Is there some other basic commands that rely upon the compare function?

I don't think so. I really have no idea what's your problem is.


Simon S(Posted 2005) [#6]
Okay, this is getting silly. Ran it again this morning without changing any code and now a single bullet is capable of deleting every player and grunt. These are the bugs that really annoy me. Random effects without changing code.

I could write a workaround (remove comapre and have a master draw list type to z sort), but I'd really like to know what on earth I've done here.

EDIT: oops, Forgot I had changed something before bedtime. On further study, alternative code for the compare method either means nothing or everything is deleted. removing the compare method entirely means the create/delete part of the code works exactly as intended.


Simon S(Posted 2005) [#7]
Update: Rather than have a master killlist to remove everything after draw, collision detection ect, I decided to do away with the list in case I had created some problem by using it, or wasn't passing the correct methods.

Nothing changed, it acted exactly as before. Still, at least that's one possible cause eliminated.


Simon S(Posted 2005) [#8]
Okay, been trying to figure this out over the last few days with little success. The only thing I've discovered is that when it deletes a shot, it also deletes any being on the same x or y.

Still unsure though, how compare alters the type without ever being called. Pretty sick of the problem to be honest, I guess I'll try for a few more days then just give up on it. Then I guess I'll try a totally side on game that won't require sorting for my next project. Once I have a better understanding of Blitzmax, I may finally discover what I've done wrong here.


skidracer(Posted 2005) [#9]
I ran into something like this the other day when I modified the compare method for sorting and completely nuked the behavior of FindLink which is used for among other things TList:Remove...


Simon S(Posted 2005) [#10]
Ah, thanks. This'll be why listremove(blah,x) and blah.remove both fail in the same way. Sounds like a bug (though I admit there could well be something I'm not grasping about how compare is supposed to work.)

Has there been any news on on this from Mark? Or would you folks recommend I just go ahead and write a workaround (remove compare and just repopulate a draw list each frame using the classic B2D style sorts?)


marksibly(Posted 2005) [#11]
Hi,

Yep, TList.Remove uses Object.Compare, which is why you're getting weird results. In fact, the version you posted doesn't delete anything because Compare never returns 0! Should it behave like this or just use '='? I'm not sure yet, but this side of things does need some work.

Anyways, the best solution is probably to store the TLink returned by TList.AddLast, and use Remove() directly on that, eg:

Field link:TLink
.
.
.
link=globallist.AddLast( self ) 'add self to globallist
.
.
.
link.Remove 'remove self from globallist

This will be fastest, and allows you to keep using Compare for sorting if you want.


Simon S(Posted 2005) [#12]
Aha, so I'm not going mad, there are other parts of the code that rely on compare. Thanks for confirming that.

Also to be fair I'd altered the code all sorts of ways, but never actually updated in the example above, so I wasn't exactly making it easy for people to help me.

I'll try your changes when I get home. Thanks for the response.


Simon S(Posted 2005) [#13]
Yeah, it works a treat. Thanks a lot for the help.