Compare messes up lists??

BlitzMax Forums/BlitzMax Beginners Area/Compare messes up lists??

Hi All,

Ive got a type called TSettler which is in a list TSettlerList.

I add a new settler to the list by:

Type TSettlersList Extends TList
	Method AddNew:TSettler(x:Int, y:Int, image:TGameImage, what:String)
		Local s:TSettler = TSettler.Create(image, x, y)
		s.settlers = Self 'point to this list	
		s.what = what
		s.setUpIdleAnimation()
		AddLast(s)
		Return s
	End Method


And in TSettler I have got this bit of code:

Method Compare:Int(other:Object)
  	Return y - TSettler(other).y
End Method


Which helps the z-ordering when drawing the settlers on screen... but now when I want to remove a settler from the list it removes the wrong one!?!?!

Method Kill()
   If settlers <> Null Then 
       settlers.Remove(Self)
       settlers = Null 'prevent circular reference
   EndIf
End Method


Any ideas?

Thanks!

The compare method, afaik, should only return -1, 0, or 1.

Cheers Gfk... well the return type for the method is an int...

Here is some runnable code from my other post, Ive added TPersonList and TPerson.

Click on the buildings (white squares) to fill up all the slots (green ovals) with people (yellow squares)... then click on the people to remove/delete them (start with the people to the right-hand side of the houses)... try this with and without the compare method....

SuperStrict

Graphics 800,600,0

Type TSlot
	Field x%,y%
	Field empty%
	Field slotList:TSlotList
	Field person:TPerson
	
	Method clone:TSlot(sl:TSlotList )		
		Local s:TSlot = New TSlot
		s.x = x
		s.y = y
		s.empty = empty
		s.slotList = sl
		DebugLog s.x
		Return s
	End Method
End Type

Type TSlotList Extends TList
	Method AddSlot:TSlot(x:Int, y:Int)
		Local s:TSlot = New TSlot
		s.x = x
		s.y = y
		s.empty = True
		s.slotList = Self
		AddLast(s)
		Return s
	End Method
	
	Method findFreeSlot:TSlot()
		For Local s:TSlot = EachIn Self
			If s.empty Then Return s
		Next
		' reset slots if all are full
		For Local s:TSlot = EachIn Self
			 s.empty = True
		Next
		Return Null
	End Method
End Type

Type TBuildingInfo
	Field name$ {Clone}
	Field slots:TSlotList{NoClone}
	Field buildingInfoList:TBuildingInfoList {NoClone}
	
	Method clone:TBuildingInfo()
		Local b:TBuildingInfo =  TBuildingInfo (CloneObject(Self))
		b.slots = New TSlotList
		For Local s:TSlot = EachIn Self.slots
			Local ss:TSlot  = s.clone(slots)
			b.slots.AddSlot(ss.x,ss.y)
		Next
		b.buildingInfoList = Self.buildingInfoList
		Return b
	End Method
End Type

Type TBuildingInfoList Extends TList
	Method AddNew:TBuildingInfo(name:String)
		Local b:TBuildingInfo = New TBuildingInfo
		b.name = name		
		b.slots = New TSlotList
		
		b.buildingInfoList = Self
		
		AddLast(b)
		Return b
	End Method
	
	Method Find:TBuildingInfo(name:String)
		For Local i:TBuildingInfo = EachIn Self
			If Upper(i.name) = Upper(name) Then Return i
		Next		
		Return Null
	End Method
End Type
	
Type TBuilding
	Field x%,y%
	Field h%,w%
	Field info:TBuildingInfo
	Field name$
	Field buildings:TBuildingList 'points to list it's part of
	
	Function Create:TBuilding(tx%, ty%,name$)
		Local b:TBuilding = New TBuilding
		b.x = tx
		b.y = ty
		b.h = 100
		b.w = 100
		b.name = name
		b.info = New TBuildingInfo
		b.info.slots = New TSlotList 
		Return b
	End Function
End Type

Type TBuildingList Extends TList
	Method AddNew:TBuilding(x%,y%,name$)
		Local b:TBuilding = TBuilding.Create(x, y,name)
		b.buildings = Self 'point to this list		
		AddLast(b)
		Return b
	End Method
	
	Method Draw()
		For Local b:TBuilding = EachIn Self
			SetColor 255,255,255
			DrawRect b.x,b.y,b.h,b.w
			SetColor 255,0,0
			DrawText b.name, b.x,b.y
			DrawText b.info.name, b.x,b.y+10
			For Local s:TSlot = EachIn b.info.slots
				If s.empty SetColor 0,255,0 Else SetColor 255,0,0
				DrawOval b.x+s.x , b.y+s.y , 10 , 10
			Next
		Next
		SetColor 255,255,255		
	End Method
	
	Method find:TBuilding(name$)
		For Local b:TBuilding = EachIn Self
			If Upper(b.name) = Upper(name) Return b
		Next
		Return Null
	End Method
End Type


Type TPerson
	Field slot:TSlot
	Field x%,y%
	Field persons:TPersonList
	Field name$
	Field building:TBuilding
	
	Function Create:TPerson(tx%, ty%,name$)
		Local b:TPerson = New TPerson
		b.x = tx
		b.y = ty
		b.name = name
		Return b
	End Function
	
	Method Compare:Int(other:Object)
  		Return y - TPerson(other).y
	End Method
	
	Method kill()
		If persons<> Null Then 'important protection
			If slot <> Null slot.empty = True
			persons.Remove(Self)
			persons= Null 'prevent circular reference
		EndIf
	End Method
End Type

Type TPersonList Extends TList
	Method AddNew:TPerson(x%,y%,name$)
		Local p:TPerson = TPerson.Create(x,y,name)
		p.persons = Self
		addlast(p)
		Return p
	End Method
	
	Method draw()
		For Local p:TPerson = EachIn Self
			SetColor 255,255,0
			DrawRect p.x,p.y,12,12
			SetColor 0,0,0
			DrawText p.name,p.x,p.y
		Next
		SetColor 255,255,255
	End Method
	
End Type

' set up objects

Local persons:TPersonList = New TPersonList
persons.AddNew(10,10,"F")
persons.AddNew(30,10,"B")

Local infoList:TBuildingInfoList = New TBuildingInfoList 
Local info:TBuildingInfo = infoList.AddNew("HOUSE")
info.slots.AddSlot(-20,10)
info.slots.AddSlot(20,120)
info.slots.AddSlot(50,130)
info.slots.AddSlot(70,120)
info.slots.AddSlot(120,10)

info:TBuildingInfo = infoList.AddNew("BAR")
info.slots.AddSlot(-20,10)
info.slots.AddSlot(50,120)
info.slots.AddSlot(120,10)


Local buildings:TBuildingList = New TBuildingList
Local b:TBuilding
b = buildings.AddNew(100,100, "HOUSE")
Local houseInfo:TbuildingInfo = infoList.Find("HOUSE")
b.info = houseinfo.clone()
b.info.name = "1st House"

b = buildings.AddNew(300,300, "HOUSE")
b.info = houseinfo.clone()
b.info.name = "2nd House"

b = buildings.AddNew(500,100, "BAR")
Local barInfo:TbuildingInfo = infoList.Find("BAR")
b.info = barInfo.clone()
b.info.name = "The Bar"


Repeat
	If MouseHit(1)
		For Local b:TBuilding = EachIn buildings
			If RectsOverlap(MouseX(),MouseY(),1,1,b.x,b.y,b.w,b.h)
				Local freeSlot:TSlot = b.info.slots.findFreeSlot()
				If freeSlot <> Null
					freeslot.empty = False
					Local p:TPerson = persons.AddNew(b.x+freeslot.x,b.y+freeslot.y,"T")
					p.slot = freeSlot
					p.building = b
					freeSlot.person = p
				End If
			EndIf
		Next
		
		For Local s:TPerson = EachIn persons
			If RectsOverlap(MouseX(),MouseY(),1,1,s.x,s.y,12,12)
				s.kill()
			EndIf
		Next
		
		
	End If

	Cls
		buildings.Draw()
		persons.draw()
	Flip
Until KeyHit(KEY_ESCAPE)

Function RectsOverlap:Int(x0:Float, y0:Float, w0:Float, h0:Float, x2:Float, y2:Float, w2:Float, h2:Float)
	If x0 > (x2 + w2) Or (x0 + w0) < x2 Then Return False
	If y0 > (y2 + h2) Or (y0 + h0) < y2 Then Return False
	Return True
End Function

' Clones an object and returns the clone. - by Azathoth
' Any fields that references an object only gets the reference copied unless MetaData contains {Clone}
' which then a copy is also made of the object referenced.
' {NoClone} prevents the field being copied.
Function CloneObject:Object(obj:Object)
	Local cobj:Object
	
	If obj=Null Then Return Null
	
	Local objId:TTypeId=TTypeId.ForObject(obj)
	
	If objId.ExtendsType(StringTypeId)
		Return String(obj)
	EndIf
	
	If objId.ExtendsType(ArrayTypeId)
		If objId.ArrayLength(obj)>0
			cobj=objId.NewArray(objId.ArrayLength(obj))
			
			If cobj
				For Local i:Int = 0 Until objId.ArrayLength(obj)
					If objId.ElementType().ExtendsType(ArrayTypeId) Or objId.ElementType().ExtendsType(StringTypeId) ..
						Or objId.ElementType().ExtendsType(ObjectTypeId)
						objId.SetArrayElement(cobj,i,CloneObject(objId.GetArrayElement(obj,i)))
					Else
						objId.SetArrayElement(cobj,i,objId.GetArrayElement(obj,i))
					EndIf
				Next
			EndIf
		EndIf
		
		Return cobj
	EndIf
	
	cobj=New obj
	
	For Local fld:TField=EachIn objId.EnumFields()
		Local fldId:TTypeId=fld.TypeId()
		If fld.Get(obj)<>Null And fld.MetaData("NoClone")=Null
			DebugLog fld.name()+"  "+fld.MetaData()+"  " fld.get(obj)
			If Not fld.MetaData("Clone")=Null
				fld.Set(cobj,CloneObject(fld.Get(obj)))
			Else
				fld.Set(cobj,fld.Get(obj))
			EndIf
		EndIf
	Next
	
	Return cobj
	
EndFunction


Does anyone know how to fix this?

No, compare:
Returns a value less than 0 if an object is less than another object, a value greater than 0 if an object is greater than another object or the value 0 if an object equals another object.


However, with your code, if two TSettlers have the same y they are treated as the same. You should use something like:
Method Compare:Int(other:Object)
  	Local s:TSettler = TSettler(other)
  	If y = s.y Return Super.Compare(other)
  	Return y - s.y
End Method


THANK YOU!!!!

Just tested with my example code... and works great!

Ive spent most of the day tracking down this problem (finally noticed it was the compare method), but didnt know what was wrong!

Thanks again!

What does super.compare perform?