Little OOP Design Problem

BlitzMax Forums/BlitzMax Programming/Little OOP Design Problem

I'm hunting a elegant solution to a complex OOP design and yet it is still important to keep it simple.

So the idea I'm working with is to make use of objects in
a way that most parts of the code can be reused and
modular to a maximum.

EDIT: Look 4 posts below, ignore this following example.




Here is a selected part of the overall idea:
Type TShip
	Field Form:TRender2D = TRender2D.CreateForm( Self )
This is the visual representation of the ship, lying in the render-object called Form which looks like this:
Type TRender2D

	Field Owner:TObject
	Field X,Y
	Field Size = 4

	Function CreateForm( Owner:TObject )
		Local Render:TRender2D = New TRender2D
		Render.Owner = Owner
	EndFunction
	
	Method Update() ' Update position from Owner       
		DrawOval X-Size/2,Y-Size/2,Size,Size 
	EndMethod
			
EndType
'This is where the problem lies, I can never know for sure that the owner object (TShip in this case) have fields called X or Y. However anything that can be draw to the screen have to have a position and optinally even rotation.

I see two solutions to this, first is to embed the renderobject in TShip or in a TEntity type, and force the object to use the image's X,Y values. The downside is that it would mean any update code such as speed would look like: image.X :+ speed, and I want it to be X:+ Speed..

Another way is to have an entity type that Tship and any other object to be rendered have to inherit. And then I can pass TEntity to the Renderobject and it can use Owner.X. Which is pretty elegant, and for this example it would most likley be the perfect solution. Consider us having some other object that is not derived from TEntity, but it still has a X and Y field, or XPoss, YPoss. I would like to still be able to render that object (this way) without changing that object. Int pointers seemed to be removed, perhaps someone could suggest any smart way to do this, or any general tips?

The later way would be the way to go.

Don't see why there should be an object on screen that does not inherit from TEntity as Tentity in full abstraction is a point with position and rotation ... so anything that can be positioned and rotated ... :-)
Do you have a specific example what might fall in there?

Or TShip could just extend TRender2D?

Hm.. Yes, I guess anything who are to be drawn can extend TEntity, or extend TRender2D directly.

It does make sense that all objects that are to be rendered extends (somewhere in the line) from some renderobject. The basic of the idea of rendering was that ANY 2D object to be rendered need to have a position X,Y and a direction, deciding that by inheritance actually seems to be a logical step when I think about it.

I'll see where this leads from here :)

Thanks for the help

Unfortunatly extending Render2D is not an option and it only works in the short run. I need a way to pass an object's field to another objects field. Doing it with as much OOP and as little code as possible.

This code below works, and it does exactly what I try to do. Yet I would like to make it simpler, less code, or in a more elegant way.

See below, is it possible to accomplish the same thing with more OOP or some smart technique? Note especially that I have to create a new find method in every new plant-type I create, which is a bit annoying ;)
Strict
Type TPlant 
	Global PlantList:TList = CreateList()
	Field Name$ = "TPlant"
	Method New()
		PlantList.Addlast(Self)
	EndMethod
	Function Find:TPlant() EndFunction
EndType

Type TTree Extends TPlant
	Field Bark = 22
	Field Name$ = "TTree"
	Function Find:TTree ()
		For Local Plant:TTree = EachIn PlantList
			Print "Found Plant "+Plant.Name
			Return Plant
		Next
		Print "Not Found "
	EndFunction
EndType

Type TOak Extends TTree
	Field A
	Field Name$ = "TOak"
	Function Find:TOak () 
		For Local Plant:TOak = EachIn PlantList
			Print "Found Plant "+Plant.Name
			Return Plant
		Next
		Print "Not Found "
	EndFunction
EndType

Type TPalm Extends TTree
	Field B
	Field Name$ = "TPalm"	
	Function Find:TPalm () 
		For Local Plant:TPalm = EachIn PlantList
			Print "Found Plant "+Plant.Name
			Return Plant
		Next
		Print "Not Found "
	EndFunction
EndType

Type TFlower Extends TPlant
	Field C
	Field Name$ = "TFlower"	
	Function Find:TFlower () 
		For Local Plant:TFlower = EachIn PlantList
			Print "Found Plant "+Plant.Name
			Return Plant
		Next
		Print "Not Found "
	EndFunction
EndType

Type TTulip Extends TFlower
	Field D
	Field Name$ = "TTulip"	
	
	Function Find:TTulip()
		For Local Plant:TTulip = EachIn PlantList
			Print "Found Plant "+Plant.Name
			Return Plant
		Next
		Print "Not Found "
	EndFunction
EndType

' 1. Create a lot of plants - They are added to a TList
' 2. Set a variable to a plant in this list of the same type
' Example: local NewFlower:TFlower = FindPlant( Newflower )
 'Looks for any plant in the list of the TFlower type.

' Only add one of each type
Local Oak:TOak = New TOak
Local Palm:TPalm = New TPalm
Local Flower:TFlower = New TFlower
Local Tulip:TTulip = New TTulip
'			
' TESTING
'________________________
Local Tulip_Mirror:TTulip
Tulip_Mirror = TTulip.Find()

Local Flower_Mirror:TFlower
Flower_Mirror = TFlower.Find()

Local Oak_Mirror:TOak
Oak_Mirror = TOak.Find()

' Print some pointless values
'____________________________
If Tulip_Mirror
	Tulip_Mirror.C = 33
	Tulip_Mirror.D = 2	
	Print Tulip_Mirror.C
	Print Tulip_Mirror.D	
EndIf

If Flower_Mirror
	Flower_Mirror.C = 1
	Print Flower_Mirror.C
EndIf

If Oak_Mirror
	Oak_Mirror.A = 1
	Print Oak_Mirror.A
	Print Oak_Mirror.Bark
EndIf


Well for a start why are you overriding the Name Field in the base TPlant?
If TTree extends TPlant it already has a field name

Second you sould be overriding the New() method each child type of TPlant, 1) to call the earlyer news 2)create a Tlist for each type

Overriding the name field is not a part of my final solution, it was a lazy way to not require to set the "default" name in the new() method. It works just the same I think.

Second the TTree and TFlower should probably be abstract.

The idea is that each of this objects is created once and all is put in a list once. A list with a lot of different objects of all different types.

The problem I have is that I need to search a TList for a object-TYPE not a specific object instance.

For example in TFlower I just want ANY TFlower that is in the global TList. When I have it I have successfully linked my Flower-Reference to the Base-Flower.

I'm not sure how to explain it really.. perhaps this is as good as it gets..

I still dont get why you dont have a tlist of all TPlants (for example), and Another Tlist for all TTree. So that All TTrees are in the TPlant list, but in there own list as well.

And I dont think that overriding Name is the same as using the base name, For one it means that there is an extra pointer in each variable (Ie each instance is bigger)

Yea, but this example is to show the problem. I'm not really using plants like this :)

However if the above can be simplified by some smart inheritance then I could apply it to my engine the way I want it.

It might be that there is no way to put the eachin loop in TPlant, then I'll just have to live with it.

To be honest Ive been reading the thread, and I only joined in when I felt I understood what you wanted to do. But I still Dont.
If we look at your first post, Every type that contains render must have a x and a y , because render2d has an x and a Y. So I didnt understand the Problem.
The you gave a simplyfied example, that had (in my opinion) several oop mistakes.
I think that the problem is that you think OOP means simpler code, when in reality OOP means More code.

IF YOU ARE IN THE HABIT of putting each new type instance into a Tlist, then do this for them all.
Or you can put the Eachin Loop into Tplant by
Putting,
field WhatType into the base
then
setting whattype in each inherited type
const type_plant =2
new() WhatType = Type_plant
(I cannot be bothered with syntax)

Then when you loop though all the base types, one of its field tells you what the child actualy is

Someone already had a similar problem. Keeping a single list and being able to get a list of a type including children. I suggested using an adjacency list which is a list of lists (or an array of lists in this case) and I made a sloppy little example.

It's the 2nd to last post
http://www.blitzbasic.com/Community/posts.php?topic=51012

I think that's more or less what you're looking for, but I'm not quite sure.

Great thread, a lot to read. It seems it is about the same problem I am struggeling with. I'll comment in a while. Thank you very much Cajun!

If I could find a way to check:
If A:TPlant equal the very same derivied object-type B:TPlant, then.. For example If Palm(A) = Palm(B), but in general for any specific TPlant-type.

Cajun, this is your code from that thread. It might be a solution but to me it looks very complex. Can you explain it?

If I'm going to use it, what I am particulary interested in is to make any "new" types you make as simple as possible. In other words to keep as much of the "engine" in the inherited types, instead of all objects that is derivied.

Hm..

'Adjacency List Technique example

Const TYPE_COUNT = 6
Const TYPE_A = 0
Const TYPE_B = 1
Const TYPE_C = 2
Const TYPE_D = 3
Const TYPE_E = 4
Const TYPE_F = 5


Type AdjList
	Field loopStart:Int
	Field theLists:AdjNode[]
	
	Method New()
		theLists = New AdjNode[TYPE_COUNT]
		For Local i:Int = 0 To TYPE_COUNT-1
			theLists[i] = New AdjNode
		Next
	EndMethod
	
	Method addRelationShip(p:Int, c:Int)
		theLists[p].addChild(c)
	EndMethod
		
	Method addObject(o:AdjType)
		theLists[o._type].addObject(o)
		If o.p_Type = -1 Then Return
	EndMethod
	
	Method removeObject(o:AdjType)
		theLists[o._type].removeObject(o)
	EndMethod
	
	Method onlyThisType:Object[]()
		Return theLists[loopStart].aList
	EndMethod
	
	Method setLoopStart(i:Int)
		loopStart = i
	End Method
	
	Method ObjectEnumerator:AdjIterator()
		Local temp:AdjIterator = New AdjIterator
		temp.theLists = theLists
		temp.n_Index = loopStart
		temp.Init()
		Return temp
	EndMethod
				
EndType

Type AdjNode
	Field children:Int[]
	Field aList:AdjType[]
	Field num:Int
	
	
	Method addObject(o:AdjType)
		If num=aList.length Then grow()
		aList[num]=o
		o.o_Index = num
		num:+1
	End Method
	
	Method removeObject(o:AdjType)
		If o.o_Index > aList.length-1 Then Return
		num:-1
		aList[num].o_Index = o.o_Index
		aList[o.o_Index] = aList[num]
		o.o_Index = -1
	EndMethod
	
	Method addChild(i:Int)
		children = children[..children.length+1]
		children[children.length-1] = i
	EndMethod
	
	Method grow()
		aList = aList[..aList.length+10]
	EndMethod
			
EndType

Type AdjIterator
	Field n_Index:Int
	Field o_Index:Int
	Field theLists:AdjNode[]
	Field que:intQue
	
	Method New()
		que = New intQue
	EndMethod
	
	Method Init()
		For Local i:Int = 0 To theLists[n_Index].children.length-1
			que.push(theLists[n_Index].children[i])
		Next
	EndMethod
	
	Method HasNext:Int()
		If o_Index < theLists[n_Index].aList.length Then Return True
		Repeat
			If Not que.isEmpty() Then
				n_Index = que.pop()
				o_Index = 0
				For Local i:Int = 0 To theLists[n_Index].children.length-1
					que.push(theLists[n_Index].children[i])
				Next
				If theLists[n_Index].aList.length > o_Index Then Return True
			Else
				Return False
			End If
		Forever
	EndMethod
	
	Method NextObject:Object()
		o_Index:+1
		Return theLists[n_Index].aList[o_Index-1]
	EndMethod
	
EndType


	
Type AdjType
	Field _Type:Int
	Field p_Type:Int
	Field o_Index:Int
	
	Method Init() Abstract
	Function ForList:Object() Abstract
End Type



Type intQue
	Field que:Int[]
	Field front:Int
	Field back:Int
	Field num:Int
	
	Method New()
		que = que[..5]
	EndMethod
	
	Method push(i:Int)
		If isFull() Then Return
		que[back]=i
		back:+1
		back:*(back < que.length)
	EndMethod
	
	Method pop:Int()
		If isEmpty() Then Return
		If front = que.length-1 Then
			front = 0
			Return que[que.length-1]
		Else
			front:+1
			Return que[front-1]
		End If
	End Method
	
	Method isEmpty:Int()
		If (front = back) And (num = 0) Then
			shrink()
			Return True
		End If
		Return False
	EndMethod
	
	Method isFull:Int()
		If (front = back) And (num <> 0) Then grow()
		Return False
	EndMethod
	
	Method grow()
		que = que[..que.length+5]
	EndMethod
	
	Method shrink()
		que = que[..5]
	EndMethod
EndType


			
Type A Extends AdjType
	Global List:AdjList
	
	Method Init()
		_Type=TYPE_A
		p_Type=-1
		List.addObject(Self)
	EndMethod
	
	Function ForList:AdjList()
		List.setLoopStart(TYPE_A)
		Return List
	EndFunction
	
	Method setupRelation()
		If p_Type > -1 Then List.addRelationship(p_Type,_Type)
	EndMethod
	
EndType



Type B Extends A

	Method Init()
		_Type=TYPE_B
		p_Type=TYPE_A
		List.addObject(Self)
	EndMethod
	
	Function ForList:AdjList()
		List.setLoopStart(TYPE_B)
		Return List
	EndFunction
EndType

Type C Extends A
	
	Method Init()
		_Type=TYPE_C
		p_Type=TYPE_A
		List.addObject(Self)
	EndMethod
	
	Function ForList:AdjList()
		List.setLoopStart(TYPE_C)
		Return List
	EndFunction
End Type

Type D Extends C
	Method Init()
		_Type=TYPE_D
		p_Type=TYPE_C
		List.addObject(Self)
	EndMethod
	
	Function ForList:AdjList()
		List.setLoopStart(TYPE_D)
		Return List
	EndFunction
End Type

Type E Extends C
	Method Init()
		_Type=TYPE_E
		p_Type=TYPE_C
		List.addObject(Self)
	EndMethod
	
	Function ForList:AdjList()
		List.setLoopStart(TYPE_E)
		Return List
	EndFunction
End Type


Type F Extends E
	Method Init()
		_Type=TYPE_F
		p_Type=TYPE_E
		List.addObject(Self)
	EndMethod
	
	Function ForList:AdjList()
		List.setLoopStart(TYPE_F)
		Return List
	EndFunction
End Type

A.List = New AdjList
	
Local aa:A = New A 
Local bb:B = New B
Local cc:C = New C
Local dd:D = New D
Local ee:E = New E
Local ff:F = New F

aa.Init()
aa.setupRelation()
bb.Init()
bb.setupRelation()
cc.Init()
cc.setupRelation()
dd.Init()
dd.setupRelation()
ee.Init()
ee.setupRelation()
ff.Init()
ff.setupRelation()

aa = New A
bb = New B
cc = New C
dd = New D
ee = New E
ff = New F

aa.Init()
bb.Init()
cc.Init()
dd.Init()
ee.Init()
ff.Init()


Print "---A---"
For aa = EachIn A.ForList()
	Print aa._Type
Next

Print
Print "---B---"
For bb = EachIn B.ForList()
	Print bb._Type
Next

Print
Print "---C---"
For cc = EachIn C.ForList()
	Print cc._Type
Next

Print
Print "---A---"
For aa = EachIn A.ForList().onlyThisType()
	Print aa._Type
Next
Print
Print "---D---"
For dd = EachIn D.ForList()
	Print dd._Type
Next

Print
Print "---E---"
For ee = EachIn E.ForList()
	Print ee._Type
Next

Print
Print "---F---"
For ff = EachIn F.ForList()
	Print ff._Type
Next



Sorry about the late reply. I was trying my hand at some linux installation and I had... troubles. Finally got it to work though.

Anyway this can probably be cleaned up (a lot). To use it as is though you need to do the following.
-The setupRelation() method only needs to be implimented once in the first object, and needs to be called once for the first created object of a type. This is just so the list knows what is a parent of what.
-The init method could be replaced by a new method... i can't remember why i didn't do that in the 1st place.
-The ForList function could be implemented once I think.
Both of the following could probably be implemented as global mapping of some kind so not every object would be carrying the same information.
-p_Type is the id of the parent object should be set in the new method
-o_Index is used internally by the list

It's late right now so in the morning I'll rework it and see what I can to to make it more userfriendly and efficient.

Thanks, take your time. I got another idea to add next and previous fields to the super type and kinda make my own list system, but this one looks more promising and seems to work already :)

Making my types into lists did not solve anything, but I think I have manage to get on the right side of things anyway. I have not understood and tested the Adjacency List Technique, but the above I tried to do is simply not possible in Blitzmax, perhaps not in any OOP language. I think the final solution looks much better, and is also much easier to manage and work with.

Cajun, I'm still interested in Adjacency Lists. If you rework it a bit, perhaps you could write a few sentences explaining the advantage of using it and perhaps some example situations where it would be more beneficial than a normal List.

To be honest I haven't touched a computer since I last posted and it feels kinda good :) ,but I'll get to it after I write something about why you would want to use them and reinstall bmax.

Adjacency Lists are mostly used for what the name implies, finding something that's adjacent so something. For example lets say you have a map of the world and we want to keep track of flights between major cities we would need a list for each city of destinations, but most of the time to get from city A to city D you can't get right there in one motion. You have go through C and B. With an AdjList you'd know that A->C->B->D with minimal searching and checking.

In the same way in a hirearcy of types you want to know all the children of A including the children of the children it's not obvious whose a child of whom in a plain old list with a constant. You'd have to loop though the whole thing every time to find what you're looking for if you even know exactly what you're looking for.

You need a child of A and let's say that B,C,E,F,J,X extend A. You would have to not only check every item in the list but check every item in the list for every child...
If _Type = B Then.... Else If _Type = C Then... etc.

Bottom Line: Adjacency lists are good for knowing how an object relates to another if at all. For a small number of object instances ( < 50 ) or object types ( <5 ) I would consider doing without, but for large amounts of data it's exponentially faster than a notmal list.

Extra Info: Also cost can be implemented into the list so that you could find a cheapest route like in the city/flight example. Or take an RTS game the AI would want to know not only cost in time to move from A to B but also potential damage cost so the cheapest route could mean 2 different things.

Ok I went through it and some of the things I thought I could get rid of were because I've been in C++ for the past 9 months. Anyway I was able to remove a field from the base AdjType that needed to be extended so that brings the foot print down to 8 bytes and if you turn them into shorts 4 bytes. I'd say that's reasonable.

Go over the code and comments and let me know what you think.

Here's the list container, it's Iterator and the type to extend
SuperStrict
'Adjacency List
'This implementation is specialized for maintaining a hierarchy using a left child - right sibling tree


'The main list object.
Type AdjList
	Field loopStart:Int			'used to work with Max's built in for...eachin
	Field theLists:AdjNode[] 	'Since the hierarchy is maintained by consts an array is required to hold the nodes for speed
	
	'no New() method because you can't pass params
	'Method New() EndMethod
	
	'Ctor for convenience
	Function Create:AdjList(n:Int = 8)
		Local temp:AdjList = New AdjList
		temp.Init(n)
		Return temp
	EndFunction
	
	'Set the size of the hierarchy to begin with
	'How many types are there?
	Method Init(n:Int = 8)
		theLists = New AdjNode[n]
		For Local i:Int = 0 To n-1
			theLists[i] = New AdjNode
		Next
	EndMethod
	
	'Add an object to the list	
	Method addObject(o:AdjType)
		theLists[o._type].addObject(o)
	EndMethod
	
	'Remove an object from the list
	Method removeObject(o:AdjType)
		theLists[o._type].removeObject(o)
	EndMethod
	
	'get a list of a specific type
	Method listOfType:AdjType[](n:Int)
		Return theLists[n].aList
	EndMethod
	
	'add a child to the parent node p
	Method addRelationship(p:Int, c:Int)
		theLists[p].addChild(c)
		theLists[c].setParent(p)
	EndMethod
	
	Method From:AdjList(n:Int)
		setLoopStart(n)
		Return Self
	EndMethod
	
	'used to work with Max's built in for...eachin
	Method setLoopStart(i:Int)
		loopStart = i
	End Method
	
	'used to work with Max's built in for...eachin
	Method ObjectEnumerator:AdjIterator()
		Return AdjIterator.Create(theLists,loopStart)
	EndMethod		
EndType


'This is the object to extended
Type AdjType
	'these could easily become shorts or even possibly bytes
	Field _Type:Int		'the type of this object
	Field p_Type:Int	'the parent type to this object. -1 = root
		
	'Standard initialization for the adj list doesn't need to be overridden
	Method Init(t:Int, p:Int = 0)
		_Type = t
		p_Type = p
	EndMethod
		
	Function ForList:Object() Abstract
End Type


'Holds the info about all the objects and their children and parents
Type AdjNode
	Field children:Int[] 		'this array holds a list of all the children types to this type
	Field aList:AdjType[]		'this is a list of the children to this type: a Linked List could make it more dynamic
	Field _type:Int				'the place of this type in the hirearchy
	Field p_type:Int
		
	Method addObject(o:AdjType)
		If _type=aList.length Then grow()
		aList[_type]=o
		'o.o_Index = _type
		_type:+1
	End Method
	
	Method removeObject(o:AdjType)
		If o._Type > aList.length-1 Then Return
		aList[_type]._type = o._type
		aList[o._type] = aList[_type]
		_type:-1
	EndMethod
	
	Method addChild(i:Int)
		children = children[..children.length+1]
		children[children.length-1] = i
	EndMethod
	
	Method setParent(p:Int)
		p_type = p
	EndMethod
	
	Method grow()
		aList = aList[..aList.length+10]
	EndMethod	
EndType

'Used with For...EachIn to do a bredth 1st traversal (all siblings are looked at before moving on to children)
Type AdjIterator
	Field n_Index:Int  			'index of the start of the iterator
	Field o_Index:Int  			'counter in theLists array to hold the iterator's place
	Field theLists:AdjNode[] 	'the list of nodes being looped through
	Field que:intQue 			'the int que to keep track of lists to check
	
	Method New()
		que = New intQue
	EndMethod
	
	'get the list to loop through and set up a que to do a bredth first traversal
	Method Init(tLists:AdjNode[],loopStart:Int)
		theLists = tLists
		n_Index = loopStart
		For Local i:Int = 0 To theLists[n_Index].children.length-1
			que.push(theLists[n_Index].children[i])
		Next
	EndMethod
	
	'make an iterator
	Function Create:AdjIterator(tLists:AdjNode[],loopStart:Int)
		Local tAI:AdjIterator = New AdjIterator
		tAI.Init(tLists,loopStart)
		Return tAI
	EndFunction
	
	Method HasNext:Int()
		If o_Index < theLists[n_Index].aList.length Then Return True
		Repeat
			If Not que.isEmpty() Then
				n_Index = que.pop()
				o_Index = 0
				For Local i:Int = 0 To theLists[n_Index].children.length-1
					que.push(theLists[n_Index].children[i])
				Next
				If theLists[n_Index].aList.length > o_Index Then Return True
			Else
				Return False
			End If
		Forever
	EndMethod
	
	Method NextObject:Object()
		o_Index:+1
		Return theLists[n_Index].aList[o_Index-1]
	EndMethod
	
EndType


'Integer que use internally by the iterator To keep track of child lists to loop through
Type intQue
	Field que:Int[]
	Field front:Int
	Field back:Int
	Field num:Int
	
	Method New()
		que = que[..5]
	EndMethod
	
	Method push(i:Int)
		If isFull() Then Return
		que[back]=i
		back:+1
		back:*(back < que.length)
	EndMethod
	
	Method pop:Int()
		If isEmpty() Then Return 0 
		If front = que.length-1 Then
			front = 0
			Return que[que.length-1]
		Else
			front:+1
			Return que[front-1]
		End If
	End Method
	
	Method isEmpty:Int()
		If (front = back) And (num = 0) Then
			shrink()
			Return True
		End If
		Return False
	EndMethod
	
	Method isFull:Int()
		If (front = back) And (num <> 0) Then grow()
		Return False
	EndMethod
	
	Method grow()
		que = que[..que.length+5]
	EndMethod
	
	Method shrink()
		que = que[..5]
	EndMethod
EndType


This is a commented example
SuperStrict

Framework brl.Basic
Import "AdjList.bmx"

'Mix up the numbers to test that order doesn't matter
Const TYPE_A:Int = 0
Const TYPE_B:Int = 7
Const TYPE_C:Int = 3
Const TYPE_D:Int = 2
Const TYPE_E:Int = 8
Const TYPE_F_1:Int = 6
Const TYPE_F_2:Int = 5
Const TYPE_F_3:Int = 1
Const TYPE_G:Int = 4

'Our base type for this list
Type A Extends AdjType
	Global List:AdjList 			'the only list object
	
	'this returns the list
	'Basicly hard code the type const
	'This function is unnessecary if you make your consts match your type names i.e. TYPE_A ~= A etc. Why? show you later
	'If you can do without it you'll minimize the stuff you have to put in every type to a single method call to Init(...)
	Function ForList:AdjList()		
		List.setLoopStart(TYPE_A)
		Return List
	EndFunction
	
	'------- Application Specific ---------
	Field xx:Int,yy:Int		'x,y coords? why not
	
	'Function to initialize out object
	'this function can be application specific
	'the type and parent and passed in but this could be hard coded in a New() Method
	Function Create:A(t:Int,p:Int,tx:Int,ty:Int)
		Local temp:A = New A
		'---- Call this -----
		temp.Init(t,p)
    	'--------------------
		temp.xx = tx
		temp.yy = ty
		List.addObject(temp) 'Optional, but we made the list for a reason...
		Return temp
	End Function
	
EndType

Type B Extends A
	Function Create:B(t:Int,p:Int,tx:Int,ty:Int)
		Local temp:B = New B
		temp.Init(t,p)	'only method that must be called within our initialization
		'x and y flip in B
		temp.xx = ty
		temp.yy = tx
		List.addObject(temp)
		Return temp
	End Function
	
	Function ForList:AdjList()		
		List.setLoopStart(TYPE_B)
		Return List
	EndFunction
EndType

Type C Extends A
	Field name:String 'lets add something to C and his children
	
	Function Create:C(t:Int,p:Int,tx:Int,ty:Int)
		Local temp:C = New C
		temp.Init(t,p)
    	temp.xx = tx
		temp.yy = ty
		List.addObject(temp)
		Return temp
	End Function
	
	Method setName(n:String)
		name = n
	EndMethod
	
	Function ForList:AdjList()		
		List.setLoopStart(TYPE_C)
		Return List
	EndFunction
End Type

Type D Extends C
	
	Function Create:D(t:Int,p:Int,tx:Int,ty:Int)
		Local temp:D = New D
		temp.Init(t,p)
    	temp.xx = tx
		temp.yy = ty
		List.addObject(temp)
		Return temp
	End Function
	
	Method setName(n:String)
		name = "D"+n+"D"
	EndMethod
	
	Function ForList:AdjList()		
		List.setLoopStart(TYPE_D)
		Return List
	EndFunction
End Type

Type E Extends C
	Method New()
		Init(TYPE_E,TYPE_C)
    EndMethod

	Function Create:E(t:Int,p:Int,tx:Int,ty:Int)
		Local temp:E = New E
		'temp.Init(t,p)			'only method that must be called within our initialization
		'we hard coded them into this type for demo purposes
		temp.xx = tx
		temp.yy = ty
		List.addObject(temp)
		Return temp
	End Function
	
	Method setName(n:String)
		name = "EEEEEEE- "+n+" -EEEEEEEEE" 
	EndMethod
	
	Function ForList:AdjList()
		List.setLoopStart(TYPE_E)
		Return List
	EndFunction
End Type

'With this type I will show that the list can be purely logical
'In other words you could have one type and implement a hierarchy logically
'This is why you may not want to hard code _Type and p_Type in your objects
'Has to extend C since it's the furthest up object that can represent the most of it's data
'I wouldn't mix my types like this where one type has 3 different 'types' in the list and each type is a truely unique type
Type F Extends C
	Field whichF:Int 'were's gonna have 3 f types
	
	Function Create:F(t:Int,p:Int,tx:Int,ty:Int)
		Local temp:F = New F
		temp.Init(t,p)			'only method that must be called within our initialization
		temp.xx = tx
		temp.yy = ty
		List.addObject(temp)
		Return temp
	End Function

	Method setName(n:String)
		name = "F"+whichF+" :-: "+n 	'more different
	EndMethod
	
	Method setF(f:Int)
		whichF = f
	EndMethod
		
	'F could be placed any where in the tree under C so we'll leave this and use another method to get only our F and lower
	Function ForList:AdjList()
		List.setLoopStart(TYPE_C) 
		Return List
	EndFunction
End Type

'Logically child of F_2 in our list
Type G Extends C
	Function Create:G(t:Int,p:Int,tx:Int,ty:Int)
		Local temp:G = New G
		temp.Init(t,p)			'only method that must be called within our initialization
		temp.xx = tx
		temp.yy = ty
		List.addObject(temp)
		Return temp
	End Function

	Method setName(n:String)
		name = "Jigga "+n
	EndMethod
	
    Function ForList:AdjList()
		List.setLoopStart(TYPE_G) 
		Return List
	EndFunction
EndType

'First make the list
A.List = AdjList.Create(9)

'Now lets setup our hierarchy 
'This could go in a function in Type A or the root of any number of such structures
'This should be set and not changed at all
A.List.addRelationship(TYPE_A,TYPE_B)
A.List.addRelationship(TYPE_A,TYPE_C)
A.List.addRelationship(TYPE_C,TYPE_D)
A.List.addRelationship(TYPE_C,TYPE_E)
A.List.addRelationship(TYPE_E,TYPE_F_1)
A.List.addRelationship(TYPE_D,TYPE_F_2)
A.List.addRelationship(TYPE_F_1,TYPE_F_3)
A.List.addRelationship(TYPE_F_2,TYPE_G)

Rem
So this is our hierarchy \ tree \ adjacency list
      A
     / \
    B   C
       / \
      D   E
      |   |
      F2  F1
      |   |
      G   F3
EndRem
 
'Now we can make some objects
Local aa:A = A.Create(TYPE_A,-1,123,456)
Local bb:B = B.Create(TYPE_B,TYPE_A,123,456)
bb = B.Create(TYPE_B,TYPE_A,123,456)
Local cc:C = C.Create(TYPE_C,TYPE_A,123,456); cc.setName("C")
Local dd:D = D.Create(TYPE_D,TYPE_C,123,456); dd.setName("D")
Local ee:E = E.Create(TYPE_E,TYPE_C,123,456); ee.setName("E")
ee = E.Create(TYPE_E,TYPE_C,123,456); ee.setName("E")
Local ff:F = F.Create(TYPE_F_1,TYPE_E,123,456); ff.setF(1); ff.setName("F")
ff = F.Create(TYPE_F_1,TYPE_E,123,456); ff.setF(1); ff.setName("F")
ff = F.Create(TYPE_F_2,TYPE_D,123,456); ff.setF(2); ff.setName("F")
ff = F.Create(TYPE_F_2,TYPE_D,123,456); ff.setF(2); ff.setName("F")
ff = F.Create(TYPE_F_3,TYPE_F_1,123,456); ff.setF(3); ff.setName("F")
ff = F.Create(TYPE_F_3,TYPE_F_1,123,456); ff.setF(3); ff.setName("F")
Local gg:G = G.Create(TYPE_G,TYPE_F_2,987,654); gg.setName("G")
gg = G.Create(TYPE_G,TYPE_F_2,987,654); gg.setName("G")
'And now we can test them


'Each loop should print itself and it's children
Print "---A---"
For aa = EachIn A.ForList()
	Print aa._Type+", "+aa.p_Type+", "+"("+aa.xx+", "+aa.yy+")"
Next

Print
Print "---B---"
For bb = EachIn B.ForList()
	Print bb._Type+", "+bb.p_Type+", "+"("+bb.xx+", "+bb.yy+")"
Next

Print
Print "---C---"
For cc = EachIn C.ForList()
	Print cc._Type+", "+cc.p_Type+", "+"("+cc.xx+", "+cc.yy+")"+", "+cc.name
Next

'from here we have to use c becuase logical parents don't match technical parents (F extends C not D or E and G extends C not F)
Print
Print "---D---"
For cc = EachIn D.ForList()
	Print cc._Type+", "+cc.p_Type+", "+"("+cc.xx+", "+dd.yy+")"+", "+cc.name
Next

Print
Print "---E---"
For cc = EachIn E.ForList()
	Print cc._Type+", "+cc.p_Type+", "+"("+cc.xx+", "+cc.yy+")"+", "+cc.name
Next

Print
Print "---F_1---"
'if we know the corresponding const we can get our list this way instead of hard coding it
'this way we could reduce the amount of code by not having a function for every type
'since we're already adding 2 fields to every type this seems like a good idea to me
For cc = EachIn F.List.From(TYPE_F_1) 
	Print cc._Type+", "+cc.p_Type+", "+"("+cc.xx+", "+cc.yy+")"+", "+cc.name
Next

Print
Print "---F_2---"
For cc = EachIn F.List.From(TYPE_F_2) 
	Print cc._Type+", "+cc.p_Type+", "+"("+cc.xx+", "+cc.yy+")"+", "+cc.name
Next

Print
Print "---F_3---"
For cc = EachIn F.List.From(TYPE_F_3) 
	Print cc._Type+", "+cc.p_Type+", "+"("+cc.xx+", "+cc.yy+")"+", "+cc.name
Next

Print
Print "---G---"
For cc = EachIn G.ForList() 
	Print cc._Type+", "+cc.p_Type+", "+"("+cc.xx+", "+cc.yy+")"+", "+cc.name
Next


Thanks!

About the use of the Adjacency list. It seems it could be of very good use for a node based AI or bot system.
Makes it both easier and faster to make decitions.

I'll go trough and check the code later today and then make a "real" reply ;)

I really liked the idea with a node based list. I find the greatest use for this in making an AI. I have still not gotten full understanding of how it works, and how I should use the list system. I think the reason I have problem to understand is that your example is quite abstract, no offence meant, I appriciate the effort very much. I tried to think of A,B,C,D,E and so on as Cities which is connected by flightpaths. Could you explain the example like that? I do not understand the use of inheritance in this, or that how I create some sort of relationship? Or can the types be totally unrelated- no inheritance? Or what if all of the types are of the same type: City but different instances of city? How did you mean that I could add a cost to each node? For example length, benifit or danger on relationships between nodes.

'With this type I will show that the list can be purely logical
'In other words you could have one type and implement a hierarchy logically
I like the sound of this. Is this by using addRelationship(a, b). Also is the relationship between instanced objects or between the base types? Can you give an practical example on that?

By having different nodes of different types, do you mean something like the node being a city, but also a base or a waypoint. All which are nodes but of different types?

Except for AI, any other good use for this?

Many thanks for sharing this, I hope I can make some great use for this. If I dynamically can create relationships between nodes and dynamically strengthen or weaken links then this has the potential to be a base of very smart AI's.

No problem on the sharing of this. I'm glad my hours of seemingly pointless Computer Science theory classes has payed off. And sorry about the abstract explanation. I blame formal education.

To really give you a full exaplination I think I should start at the beginning and I don't have much time right now. On Monday I'll come back with all the answers to your questions.

Great, thanks :)