Sortlist custom sorting function problem

BlitzMax Forums/BlitzMax Beginners Area/Sortlist custom sorting function problem

QuickSilva(Posted 2009) [#1]
I`m having a few problems implementing a custom sorting function into one of my projects.

I have a global list contained in a base type, TEntity, with all other types extended from this. Each time a new object is create in one of the other types it is added to the global list.

I need to be able to my types by a field but only if they are of a certain type, for example only if they are TAliens. I then want to sort them by one of there fields, ZOrder or something like that.

I am using the following code to try to implement this but I keep getting problems with the objects passed into the function being null. I cannot see why this is? Can anyone please help? Is it possible to tell what type an object is so that I can sort it only if it is of the required type? Is it not possible to sort only certain objects if they are all in the same global list?

Thanks for any help,
Jason.

Function SortZOrder:Int(Object1:Object,Object2:Object)
	Local Alien1:TAlien=TAlien(Object1)
	Local Alien2:TAlien=TAlien(Object2)
		
	If Alien1 Or Alien2=Null Then End
	
	If Alien1.ZOrder<Alien2.ZOrder
		Return 1
	Else
		Return -1	
	EndIf
End Function

SortList(TAlien.List,True,SortZOrder)



Warpy(Posted 2009) [#2]
Just guessing, but
If Alien1 Or Alien2=Null Then End

should be
If Alien1=Null Or Alien2=Null Then End


As it is, it's quitting if Alien1 is a valid object!


QuickSilva(Posted 2009) [#3]
@Warpy, thanks I missed that. Still no joy though :(

Jason.


TaskMaster(Posted 2009) [#4]
If the object passed in is not a TAlien, then Alien1 or Alien2 would be Null. You are not passing in objects that aren't TAliens are you?


Jesse(Posted 2009) [#5]
the reason you are not getting a null it's because of the base type. you are using the same basetype for allof them(polymorphing). I suggest you use a field in the base type that would allow your to determine what type the object is:
at the craete function fill in the field for whatever type you are creating.
when you pass the object convert the objects to the base type then test to see if the field type matches to what you want then if needed comver it to the extended type. I usually don't need to convert sence the abstracts in the base type solves that.
I can post an example if you need more help.


QuickSilva(Posted 2009) [#6]
Please do Jesse if you can spare a moment.

Jason.


Jesse(Posted 2009) [#7]
'this assumes all you are putting in the list are objects with a base type of "Tbase"
'if you store other base types in the list you do need to use "object" for the parameter and "Tbase(object) = null" comparison as an added test
SuperStrict

Type Tbase
	Field x:Int
	Field y:Int
	Field ZOrder:Int

	Field _type:Int

	Global list:TList = New TList

	Const cSHIP:Int = 1
	Const cALIEN:Int  = 2
	
	Method draw() Abstract
	Method update() Abstract
	
End Type


Type TShip Extends Tbase 

	Function Create:TShip(x:Int,y:Int,z:Int)
		Local s:TShip = New Tship
		s.x = x
		s.y = y
		s.ZOrder = z
		S._type = s.cSHIP
		s.list.addlast(s)
		Return s
	End Function
	
	Method update()
		x = x+3
		y = y+2
	End Method

	Method draw()
		DrawOval x,y,10,10
	End Method
End Type

Type TAlien Extends Tbase

	Function Create:TAlien(x:Int, y:Int,z:Int)
		Local s:TAlien = New TAlien
		s.x = x
		s.y = y
		s.zOrder = z
		s._type = s.cALIEN
		s.list.addlast(s)
		Return s
	End Function

	Method update()
		x = x+x*.1
		y = y+3
	End Method

	Method draw()
		DrawRect x,y,10,10
	End Method

End Type


Function SortZOrder:Int(alien1:Tbase,alien2:Tbase) ' you can use 'object' but then you have to convert to at least a Tbase
		
	If Alien1._type <>  Alien1.cALIEN Or Alien2._type <> Alien2.cALIEN Then Print "other object" return 0
	
	If Alien1.ZOrder<Alien2.ZOrder 'ZOrder is part of the Tbase
		Return 1
	Else
		Return -1	
	EndIf
End Function

TShip.Create(120,120,10)
TAlien.Create(215,230,12)
Talien.Create(120,87,8)



For Local a:Tbase = EachIn Tbase.list
	For Local b:Tbase = EachIn Tbase.list
		If a = b Continue
		If sortZOrder(b,a)= 1 Print "ok"
	Next
Next	

Graphics 800,600
Cls

For Local O:Tbase = EachIn Tbase.list
	O.update() ' this updates the ship and aliens
Next

For Local O:Tbase = EachIn Tbase.list
	O.draw() ' this draws the ship and aliens
Next
Flip()
WaitKey()
End


some programmers use string as the _type and constants that way they can just Print _type and it displays the relevant information. I prefer to use integers as it's a bit faster.

I hope this is clear. If not, just ask.

***************[edited]************************


Kurator(Posted 2009) [#8]
If you can live with it, that the whole list is ordered try this approach:

[edit] even if you filter your sort routine by a specific type - tlist and its sort function iterates over all items in the list, so in my opinion it is more efficient to sort the whole list [/edit]

SuperStrict

Type TEntity Abstract
	Field x:Double
	Field y:Double
	Field z:Double
	
	Field name:String
	
	Method Output() Abstract
	
	Method ZCompareTo:Int(other:TEntity)
		If Self.z < other.z
			Return -1
		Else
			Return 1
		EndIf
	EndMethod
EndType

Type TSpaceShip Extends TEntity
	
	Function Create:TSpaceShip(x:Double , y:Double , z:Double , name:String)
		Local this:TSpaceShip = New TSpaceShip
		this .x = x
		this .y = y
		this .z = z
		this .name = name	
		Return this
	EndFunction
	
	Method Output() 
		Print name + " @ "+Left(String(z),5)
	EndMethod
EndType

Type TAlien Extends TEntity
	
	Function Create:TAlien(x:Double , y:Double , z:Double , name:String)
		Local this:TAlien = New TAlien
		this .x = x
		this .y = y
		this .z = z
		this .name = name	
		Return this
	EndFunction	
	
	Method Output() 
		Print name + " @ "+Left(String(z),5)
	EndMethod
EndType

Function TEntityZSorter:Int(o1:Object, o2:Object)
	If Not TEntity(o1) Return 0
	If Not TEntity(o2) Return 0
	Return TEntity(o1).ZCompareTo(TEntity(o2))
EndFunction

Global entityList:TList = New TList

entityList.AddLast( TSpaceShip.Create(0, 0, 10, "Space Ship #1") )
entityList.AddLast( TSpaceShip.Create(0, 0, 20, "Space Ship #2") )
entityList.AddLast( TSpaceShip.Create(0, 0, 30, "Space Ship #3") )
entityList.AddLast(     TAlien.Create(0, 0, 15, "Alien      #1") )
entityList.AddLast(     TAlien.Create(0, 0, 25, "Alien      #2") )
entityList.AddLast(     TAlien.Create(0, 0, 35, "Alien      #3") )

Print "~n===> Original Order"
For Local e:TEntity=EachIn entityList
	e.Output()
Next

entityList.Sort(False, TEntityZSorter)

Print "~n===> Globally Ordered"
For Local e:TEntity=EachIn entityList
	e.Output()
Next

Print "~n===> SpaceShips Ordered"
For Local e:TSpaceShip=EachIn entityList
	e.Output()
Next

Print "~n===> Aliens Ordered"
For Local e:TAlien=EachIn entityList
	e.Output()
Next



QuickSilva(Posted 2009) [#9]
Thanks for both of those examples, Jesse and Kurator, I understand what was going wrong in my program now thanks to your code.

Thanks to all others that helped too :)

Jason.


Jesse(Posted 2009) [#10]
@Kurator
I have always been comfused by the abstract directly in the base type definition:

Type TEntity Abstract


you mind explaining its purpose? or anybody.


Kurator(Posted 2009) [#11]
Pretty simple:

- you can not instanciate an Object from an Abstract Type.

Try the following in my Code above:

"test:TEntity = New TEntity"

..that will not work - because its abstract.

The only thing ist, like in the above code, you can subclass / subtype from it.

That has some nice advantages for object orientated programming:
- All Subclasses have the same structure, fields.
- You can also define some methods, that all subclasses share (as normal subtyping)
- You can define "abstract" mehthods - these have to be implemented by the subclasses.

When an abstract type is subtyped, the subtype usually provides implementations for all of the abstract methods in its parent type. However, if it does not, the subtype must also be declared abstract.


TaskMaster(Posted 2009) [#12]
I believe abstract means that you cannot create one of these types, you must inherit it into a new type to use it.

Beat me to the punch.


Jesse(Posted 2009) [#13]
Thanks, now I know.